////
Search
Duplicate
🎉

디폴트 메서드

이 장의 내용

디폴트 메서드란 무엇인가?
진화하는 API가 호환성을 유지하는 방법
디폴트 메서드의 활용 패턴
해결 규칙
라이브러리 설계자 입장에서 인터페이스에 메서드를 추가하거나 변경하는 경우, 무제가 발생한다.
인터페이스를 구현했던 모든 클래스의 구현도 바꿔야 하기 때문이다.
자바 8에서는 이 문제를 해결하고자 기본 구현을 포함하는 인터페이스를 정의하는 두 가지 방법을 제공한다.
정적 메서드를 사용하는 것이다.
기본 구현을 제공할 수 있도록 디폴트 메서드를 사용하는 것이다.
디폴트 메서드는 라이브러리 설계자들이 주로 사용한다. 이를 사용하면 자바 API의 기존 호환성을 유지하면서 라이브러리를 수정할 수 있기 때문이다.

1. 변화하는 API

이미 릴리즈된 인터페이스를 고치면 많은 문제가 발생한다.
사용자들이 해당 인터페이스를 구현하는 클래스를 만들었다면 인터페이스에 수정이 발생했을 때, 해당 클래스들이 모두 정상적으로 동작하지 않게 되어버린다.
이렇게 공개된 API에 변화가 발생하면 기존 버전과의 호환성 문제가 발생한다.
이를 해결하기 위해 자바 8에서는 디폴트 메서드를 제공하고 있다.

2. 디폴트 메서드란 무엇인가?

자바 8에서 호환성을 유지하면서 API를 변경할 수 있도록 지원하는 새로운 기능이다.
인터페이스는 자신을 구현하는 클래스에서 메서드를 구현하지 않아도 되는 새로운 메서드 시그니처를 제공한다.
디폴트 메서드는 인터페이스를 구상 클래스에서 구현하지 않은 메서드는 인터페이스에서 기본적으로 제공한다.
default라는 키워드로 시작하며 다른 클래스에 선언된 메서드처럼 메서드 바디를 포함한다.
추상 클래스 vs 자바 8의 인터페이스
추상 클래스와 인터페이스는 뭐가 다를까?
1.
클래스는 하나의 추상 클래스만 상속받을 수 있지만 인터페이스는 여러 개 구현할 수 있다.
2.
추상 클래스는 인스턴스 변수로 공통 상태를 가질 수 있다. 하지만 인터페이스는 인스턴스 변수를 가질 수 없다.

3. 디폴트 메서드 활용 패턴

1.
선택형 메서드
자바 8 이전에는 Iterator 인터페이스의 remove 메소드는 잘 사용하지 않아 무시되었기 때문에 많은 구상 클래스들이 빈 구현을 제공했다.
디폴트 메서드를 이용하면 remove같이 중요도가 낮은 메서드에 기본 구현을 제공할 수 있으므로 구상 클래스에서 굳이 빈 구현을 제공하지 않아도 된다.
기본 구현이 제공되므로 구상 클래스는 빈 구현을 유지할 필요가 없어졌고 불필요한 코드를 줄일 수 있다.
2.
동작 다중 상속
디폴트 메서드를 이용하면 기존에는 불가능했던 동작 다중 상속 기능도 구현할 수 있다.
자바에서 클래스는 한 개의 클래스만 상속할 수 있지만 인터페이스는 여러 개 구현할 수 있기 때문이다.
다음 ArrayList 클래스의 예시를 봐보자.
public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, Serializable { ... }
Java
복사
여기서 ArrayList는 한 개의 클래스를 상속받고 여섯 개의 인터페이스를 구현한다.
결과적으로는 AbstaractList, List, RandomAccess, Cloneable, Serializable, Iterable, Collection의 자손 타입이 되는 것이다.
따라서 디폴트 메서드를 사용하지 않아도 다중 상속을 활용할 수 있다.
다중 상속을 중복되지 않는 최소한의 인터페이스로 유지한다면 우리는 코드에서 동작을 쉽게 재사용하고 조합할 수 있다.

4. 해석 규칙

자바의 클래스는 하나의 부모 클래스만 상속받을 수 있지만 여러 인터페이스를 동시에 구현할 수 있다.
자바 8에는 디폴트 메서드가 추가되었으므로 같은 시그니처를 갖는 디폴트 메서드를 상속받는 상황이 생길 수 있을텐데 이런 상황에서는 어떻게 메서드를 선택할까?
1.
알아야 할 세가지 해결 규칙
클래스가 항상 이긴다. 클래스나 슈퍼 클래스에서 정의한 메서드가 디폴트 메서드보다 우선권을 갖는다.
1번 규칙 이외의 상황에서는 서브 인터페이스가 이긴다. 상속 관계를 갖는 인터페이스에서 같은 시그니처를 갖는 메서드를 정의할 때는 서브 인터페이스가 이긴다.
즉 B가 A를 상속받는다면 B가 A를 이긴다.
여전히 디폴트 메서드의 우선순위가 결정되지 않았다면 여러 인터페이스를 상속받는 클래스가 명시적으로 디폴트 메서드를 오버라이드하고 호출해야 한다.
2.
디폴트 메서드를 제공하는 서브 인터페이스가 이긴다.
A와 B 인터페이스가 존재하고 B가 A를 상속받는 상황에서 A, B를 구현하는 클래스 C가 있다고 해보자.
A, B 인터페이스 모두 hello()라는 메소드가 존재한다면 컴파일러는 누구의 정의를 사용할까? 여기선 클래스가 없으니 서브 인터페이스인 B가 이길 것이라고 설명한다.

5. 마치며

자바 8의 인터페이스는 구현 코드를 포함하는 디폴트 메서드, 정적 메서드를 정의할 수 있다.
디폴트 메서드의 정의는 default 키워드로 시작하며 일반 클래스 메서드처럼 바디를 갖는다.
공개된 인터페이스에 추상 메서드를 추가하면 소스 호환성이 깨진다.
디폴트 메소드 덕분에 라이브러리 설계자가 API를 바꿔도 기존 버전과의 호환성을 유지한다.
선택형 메서드와 동작 다중 상속에도 디폴트 메소드를 사용할 수 있다.
클래스가 같은 시그니처를 갖는 여러 디폴트 메서드를 상속하면서 생기는 충돌 문제를 해결하는 규칙이 있다.
클래스나 슈퍼 클래스에 정의된 메서드가 다른 디폴트 메서드 정의보다 우선한다. 이 외의 상황에서는 서브 인터페이스에서 제공하는 디폴트 메서드가 선택된다.
두 메서드의 시그니처가 같고 상속관계로도 충돌 문제를 해결할 수 없을 때에는 디폴트 메서드를 사용하는 클래스에서 메서드를 오버라이드 해서 어떤 디폴트 메서드를 호출할지 명시해주어야 한다.