Skip to content

[아이템 21] 인터페이스는 구현하는 쪽을 생각해 설계하라 #29

Description

@DongLee99

자바 8 이전

자바 8 이전에는 기존 구현체를 꺠뜨리지 않고는 인터페이스에 메서드를 추가할 수 있는 방법이 없었다. ( 인터페이스에 메서드 추가 시 컴파일 오류가 나는데 이는 만약 오픈 소스코드를 만들었는데 새로운 메소드를 만들어야 할 때가 생길 수 있다. 이때 내가 잘못만든 코드로 인해 모든 사람에게 오류가 발생하는 심각한 사태가 발생할 수 있다. )

자바 8 이후

interface NewInterface {
    default void newFuntion() {
        System.out.println("new Function");
    }
}

Defaul 메소드의 등장
자바 8에 와서 기존 인터페이스에 새로운 메소드를 추가할 수 있도록 default 메소드가 생겼다. 하지만 그렇다해도 위험이 완전히 사라진 것은 아니다.

자바 7까지는 세상에서는 모든 클래스가 현재의 인터페이스에 새로운 메서드가 추가될 일은 영원히 없다. 라고 가정 후 작성되었던 코드였다. 이후 등장한 default 메소드는 이를 무시한 채 무작정 삽입될 뿐이다. 따라서 불변식을 해치지 않는 default 메소드 생성은 어려운 법이다.

default boolean removeIf(Predicate<? super E> filter) {
    Object.requireNonNull(filter);
    boolean result = false;
    for (Iterator<E> it = iterator(); it.hasNext(); ) {
        if (filter.test(it.next())) {
            it.remove();
            result = true;
        }
    }
    return result;
}

다음은 자바 8에 추가된 removeIf 메서드이다. 이 코드보다 더 범용적으로 구현하기도 어렵겠지만, 그렇다고 해서 현존하는 모든 Collection 구현체와 어울리는 것도 아니다. 아파치의 SynchronizedCollection에서 removeIf를 사용하게 된다면 여러 스레드가 공유하는 환경에서 동기화를 제공해주지 못한다. 이는 예기치 못한 결과로 이어질 수 있다.

이를 막기 위한 자바 플랫폼의 조치

구현한 인터페이스의 디폴트 메서드를 재정의하고, 다른 메서드에서 디폴트 메서드를 호출하기 전에 사전 작업을 수행하게 했다. 위의 예로는 SynchronizedCollection이 반환하는 package-private 클래스들은 removeIf를 재정의하고, 이를 호출하는 다른 메서드들은 디폴트 메서드 호출 이전에 동기화 하도록 했다.

하지만 디폴트 메서드는 기존 구현체에 런타임 오류를 일으킬 수 있다. 따라서 기존 인터페이스에 디폴트 메서드로 새 메서드를 추가하는 일은 가능한 피해야한다.

핵심

default 메서드가 생겼다 해도 인터페이스 설계할 때는 세심한 주의를 기울여야한다. 항상 자바에서 주장하는 불변을 생각하며 신중하게 설계하도록 하자.
인터페이스를 릴리즈 한 ㅎ라도 결함을 수정하는 게 가능하지만, 그 가능성에 기대서는 안 된다.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions