13장: 디폴트 메서드
디폴트 메서드의 필요성
인터페이스에 새로운 메서드를 추가하려면, 해당 인터페이스를 구현하는 모든 클래스에서 새 메서드를 구현해야 한다.
따라서 인터페이스를 구현하는 클래스가 매우 많을 경우에는 인터페이스를 변경하기 어렵다.
자바 8에서는 인터페이스 내부에 정적 메서드를 사용하거나 디폴트 메서드를 제공하도록 지원한다.
이에 따라 기존 인터페이스 구현체는 자동으로 새로운 메서드를 상속받는다.
주로 라이브러리 설계자들이 하위 호환성을 유지하면서 라이브러리를 변경하는 용도로 사용한다.
Java 의 List 클래스에도 정렬을 위해 디폴트 메서드를 추가해 사용하고 있다.
인터페이스에 새로운 메서드를 추가해도 바이너리 호환성은 유지된다. 즉, 새로 추가된 메서드를 호출하지만 않으면 클래스에서 메서드를 구현하지 않아도 기존 클래스가 잘 동작한다.
바이너리 호환성: 코드를 변경한 후 에러 없이 기존 바이너리가 실행될 수 있다.
소스 호환성: 코드를 변경해도 기존 프로그램을 다시 컴파일할 수 있다.
동작 호환성: 코드를 변경해도 같은 입력에 대해 같은 동작을 수행한다.
하지만 전체 애플리케이션을 재빌드할 때에 컴파일 에러가 발생할 것이다.
디폴트 메서드란
인터페이스에서 호환성을 유지하면서 API를 바꿀 수 있도록 제공하는 메서드
인터페이스의 구현 클래스에서 반드시 구현하지 않아도 되는 메서드이다.
만약 구현 클래스에서 디폴트 메서드를 오버라이드하지 않으면 인터페이스에서 정의된대로 수행된다.
추상 클래스와 차이점
클래스는 하나의 추상 클래스만 상속받을 수 있지만 인터페이스는 여러 개를 구현할 수 있다.
추상 클래스는 인스턴스 필드를 공통적으로 가질 수 있지만 인터페이스는 인스턴스 필드를 가질 수 없다.
활용 패턴
선택형 메서드
인터페이스를 구현하는 클래스에서 필요 없는 메서드를 일단 오버라이드하고 비워두는 대신, 디폴트 메서드에 기본 구현을 제공할 수 있다.
아래와 같이 remove가 필요 없는 클래스에서는 오버라이드하지 않고 기본 동작을 수행하도록 하고, remove가 필요한 클래스에서는 오버라이드하도록 한다.
동작 다중 상속
기능이 중복되지 않도록 최소의 인터페이스로 분리시켜 다중 상속을 받을 수 있도록 한다.
예를 들어 어떤 도형에 대해
회전, 크기 조절, 이동
의 특성을 가질 수 있다고 하면, 각각의 인터페이스를 생성할 수 있다.그리고 각 인터페이스에서는
회전하기, 크기조절하기, 이동하기
작업을 수행하는 디폴트 메서드를 만들어두면 한 번의 구현으로도 여러 구현 클래스에서 사용할 수 있고, 로직을 변경해야 할 때에도 구현 클래스에 영향 없이 변경할 수 있다.아래는 회전, 이동을 인터페이스로 정의하고, 인터페이스를 구현하는 클래스에 대한 코드이다. Monster 객체를 정의해 rotateBy나 moveHorizontally, moveVertically 메서드를 호출할 수 있다.
해석 규칙
여러 인터페이스를 구현하다보면 같은 시그니처를 갖는 디폴트 메서드를 상속받는 상황이 발생할 수 있다.
이런 상황을 잘 처리하기 위해 세 가지 해결 규칙을 따라야 한다.
3가지 규칙
클래스, 부모 클래스의 메서드가 디폴트 메서드보다 우선으로 처리된다.
추상 클래스와 인터페이스를 모두 구현하는 클래스이고, 같은 메서드 시그니처를 가진 추상 메서드와 디폴트 메서드가 존재한다면 클래스에서 반드시 추상 메서드를 구현해야 한다. 왜냐하면 인터페이스의 디폴트 메서드보다 부모 클래스의 메서드가 우선이기 때문이다.
상속 관계를 갖는 두 인터페이스를 구현할 경우, 자식 인터페이스의 디폴트 메서드가 우선으로 처리된다.
위 두 상황이 아니라면 구현 클래스가 명시적으로 디폴트 메서드를 오버라이드하고 호출해야 한다.
아래와 같이 A,B 인터페이스에 hello라는 디폴트 메서드가 존재하고 C가 두 인터페이스를 구현한다면 명시적으로 어떤 디폴트 메서드를 호출할 지 오버라이드해주어야 한다.
디폴트 메서드의 시그니처가 완전히 동일하지 않아도 어떤 디폴트 메서드를 호출해야 하는지 알기 어려운 경우 오버라이드해주어야 한다.
Last updated