본문으로 바로가기

1. 디폴트 메서드

Java 8 부터는 기존의 구현체를 깨뜨리지 않고 인터페이스에 디폴트 메서드를 추가할 수 있다. 그러나 생각할 수 있는 모든 상황에서 불변식을 해치지 않는 디폴트 메서드를 작성하기 어렵다.

1.1. SynchronizedCollection 예시

아파치의 SynchronizedCollection은 메서드를 락 객체로 동기화한 후 내부 컬랙션 객체에게 기능을 위임하는 래퍼 클래스이다. removeIf()를 재정의하고 있지 않다.

 

  • 만약 Java 8의 동기화 되지 않은 removeIf() 디폴트 구현을 물려받는다면?
    • removeIf()의 구현은 동기화를 모르기 때문에 락 객체를 사용할 수 없다.
    • 멀티 쓰레드 상황에서 호출하면 에러가 발생할 수 있다.

Java 플랫폼 라이브러리들은 구현한 인터페이스의 디폴트 메서드를 재정의하고, 다른 메서드에서는 디폴트 메서드를 호출하기 전에 필요한 작업을 수행하도록 한다.

 

  • Collections.synchronizedCollection이 반환하는 package-private 클래스들은 removeIf를 재정의한다.
  • 이를 호출하는 메서드들은 디폴트 메서드 사용 전에 동기화하도록 한다.
  • 그러나 Java 플랫폼 라이브러리가 아닌 제 3의 기존 컬렉션 구현체들은 이런 변화에 발맞춰 수정되지 않아 문제가 되었다.

1.2. 주의사항

기존 인터페이스에 디폴트 메서드를 추가하는 것은 꼭 필요한 경우가 아니면 피해야 한다. 디폴트 메서드들은 컴파일에 성공하더라도 기존 구현체에 런타임 오류를 낼 수 있다. 처음 인터페이스를 설계할 때부터 주의를 기울여야 한다.