디폴트 메서드가 있더라도
디폴트 메서드를 추가하는 데는 숙고가 필요하다. 인터페이스를 구현한 후 나중에 디폴트 메서드를 추가하면, 기존 구현체들과 연동될 것이라는 보장이 없기 때문이다.
일례로 Collections의 removeIf()가 있다. 아파치의 SynchronizedCollection 클래스는 모든 메서드를 동기화하여 호출해주는 역할을 하는데, removeIf()를 재정의하고 있지 않다. 이 상황에서 removeIf()를 호출하면, 해당 구현체가 모든 메서드에서 동기화 해주던 역할이 보장되지 못 한다.
default boolean removeIf(Predicate<? super E> filter) {
Objects.requireNonNull(filter);
boolean removed = false;
final Iterator<E> each = iterator();
while (each.hasNext()) {
if (filter.test(each.next())) {
each.remove();
removed = true;
}
}
return removed;
}
Java
복사
java 17의 Collection API
// https://mvnrepository.com/artifact/org.apache.commons/commons-collections4/4.4
public class SynchronizedCollection<E> implements Collection<E>, Serializable {
/** Serialization version */
private static final long serialVersionUID = 2412805092710877986L;
/** The collection to decorate */
private final Collection<E> collection;
/** The object to lock on, needed for List/SortedSet views */
protected final Object lock;
...
@Override
public boolean remove(final Object object) {
synchronized (lock) {
return decorated().remove(object);
}
}
/**
* @since 4.4
*/
@Override
public boolean removeIf(final Predicate<? super E> filter) {
synchronized (lock) {
return decorated().removeIf(filter);
}
}
...
}
Java
복사
Apache Commons Collection4 , 책 내용과는 달리 현재는 removeIf 를 오버라이드 해 두었다.
새로운 인터페이스를 만들 때
기존 인터페이스에 디폴트 메서드를 추가하는 경우와 달리, 새로운 인터페이스를 만들 땐 표준적인 메서드 구현을 제공하는 유용한 수단이며, 인터페이스를 더 쉽게 구현할 수 있도록 해준다.
오류를 줄이기 위한 제언
새로운 인터페이스라면 릴리스 전에 서로 다른 방식으로 최소한 세 가지는 구현하여 테스트 해봐야 한다. 또 각 인터페이스의 인스턴스를 다양한 작업에 활용하는 클라이언트도 여러 개 만들어 봐야 한다.
이렇게 해야 인터페이스를 릴리스 하기 전, 바로잡을 기회를 가질 수 있다. 인터페이스를 릴리스 한 후 결함을 수정하는 게 가능한 경우도 있겠지만, 절대 그 가능성에 기대서는 안 된다.
용어정리
한글명 | 영어명 |
후손 | posterity |