Modern Java In Action 정리 - 디폴트 메서드
- 디폴트 메서드란 무엇인가 ?
- 진화하는 API가 호환성을 유지하는 방법
- 디폴트 메서드의 활용패턴
- 해결 규칙
모던 자바 인 액션 책을 보고 정리한 글입니다.
변화하는 API
- API를 변경하는것이 왜 어려운지에 대해 예제를 통해 알아보자.
API 버전 1
- Resizable 인터페이스 초기 버전은 다음과 같은 메서드를 지원한다.
1 |
|
API 버전 2
- 요구사항의 변화로 Resizable 에 메서드가 추가되었다고 가정하자.
1 |
|
- 사용자가 겪는 문제
- Resizable을 고치면 Resizable을 구현하는 모든 클래스는 추가된 메소드 또는 변경된 메소드를 구현해야 한다.
- 인터페이스에 새로운 메서드를 추가하면 바이너리 호환성은 유지되지만 컴파일 단계에서 에러가 발생할 것이다
- 공개된 API를 고치면 기존 버전과의 호환성 문제가 발생했다. 디폴트 메서드가 나온 이후로는 이러한 문제들을 해결할 수 있게 되었다.
디폴트 메서드란 무엇인가 ?
- Java8에서는 호환성을 유지하면서 API를 바꿀수 있도록 새로운 기능인 디폴트 메서드를 제공한다.
1 |
|
- @FuntionalInterface는 오직 하나의 추상 메서드를 포함한다. 디폴트 메서드는 추상 메서드에 해당하지 않는 사실을 기억하자.
디폴트 메서드 활용 패턴
- 디폴트 메서드를 이용하는 두가지 방식, Optional Method와 Multiple Inheritance of Behavior 를 알아보자.
선택형 메서드
- 인터페이스를 구현하는 클래스에서 비어있는 메서드를 확인한 적이 있을것이다. 인터페이스에 정의된 메서드이지만 실제로 사용하지 않는 메서드라 빈 메서드를 정의해야 하는 상황이다.
- 디폴트 메서드를 이용하면 이러한 메서드에 기본 구현을 제공해줄 수 있다. Iterator 인터페이스를 살펴보자.
1 |
|
동작 다중 상속
- 디폴트 메서드를 이용하면 기존에는 불가능했던 동작 다중 상속 기능도 구현할 수있다.
- 자바에서는 한개의 클래스 상속만 가능하지만 인터페이스는 여러 개를 구현할 수 있도록 되어 있다.
1 |
|
- 다중 상속 형식
- ArrayList는 AbstractList, List
, RandomAeccess, Cloneable, Serialiizable의 서브 형식이 된다. - Java8 에서는 인터페이스가 구현을 포함할 수 있으므로 클래스는 여러 인터페이스에서 동작을 상속받을 수 있다. 중복되지 않는 최소한의 인터페이스를 유지한다면 재사용과 조합을 쉽게 할 수 있다.
- 기능이 중복되지 않는 최소의 인터페이스
- 기존의 코드를 재사용하며 새로운 기능을 제공할때 디폴트 메서드를 활용할 수 있다.
1 |
|
- 위 인터페이스는 템플릿 메서드 패턴과 비슷해보인다. 템플릿 메서드 패턴은 추상 클래스를 상속 받아 구현하게 되어 다중상속이 불가능 하다는 차이점이 있다.
- 인터페이스 조합
- 여러 인터페이스를 조합하여 기존 기능들을 재사용할 수 있다.
1 |
|
- Monster 클래스는 3가지 인터페이스의 디폴트 메서드를 자동으로 상속 받는다. 움직일수 있고, 회전가능하며 크기도 조절할 수 있는 클래스가 되었다.
반면 움직일수 있고, 회전 가능하지만 크기는 조절할 수 없는 클래스를 만드려면 2가지 인터페이스만 구현하면 될것이다. - 인터페이스에 디폴트 구현을 포함하면 또 다른 장점이 생긴다. 특정 요구사항이 변경되었을 경우 해당하는 인터페이스의 디폴트 메서드만 변경하면 상속 받는 모든 클래스에 요구사항이 반영된다.
해석 규칙
- Java 8에는 디폴트 메서드가 추가되어 같은 시그니처를 갖는 디폴트 메서드를 상속받는 상황이 생길 수 있다.
1 |
|
알아야 할 세 가지 해결 규칙
- 클래스가 항상 이긴다. 클래스나 슈퍼 클래스에서 정의한 메서드가 디폴트 메서드보다 우선권을 갖는다.
- 1번 규칙 이외의 상황에서는 서브 인터페이스가 이긴다. 상속관계를 갖는 인터페이스에서 같은 시그니처를 갖는 메서드를 정의할 때는 서브 인터페이스가 이긴다. 즉 B가 A를 상속받는다면 B가 A를 이긴다.
- 디폴트 메서드의 우선순위가 결정되지 않았다면 여러 인터페이스를 상속받는 클래스가 명시적으로 디폴트 메서드를 오버라이드하고 호출해야 한다.
정리
- Java 8의 인터페이스는 구현 코드를 포함하는 디폴트 메서드, 정적 메서드를 정의할 수 있다.
- 디폴트 메서드의 정의는 defualt 키워드로 시작하며 일반 메서드처럼 바디를 갖는다.
- 공개된 인터페이스에 추상 메서드를 추가하면 소스 호환성이 깨진다.
- 디폴트 메서드를 사용하면 소스 호환성을 유지할 수 있다.
- 선택형 메서드와 동작 다중 상속에도 디폴트 메서드를 사용할 수 있다.
- 클래스가 같은 시그니처를 갖는 여러 디폴트 메서드를 상속하며 생기는 충돌 문제를 해결하는 규칙이 있다.