[Java8 In Action] #9 디폴트 메서드 |
Java8 In Action 내용을 보며 정리한 내용입니다.
정리자는 기존에 Java8 을 한차례 rough 하게 공부한 적이 있고, Kotlin 역시 공부한 적이 있습니다.
위의 prerequisite 가 있는 상태에서 추가적인 내용만 정리한 내용이므로, 제대로 공부를 하고 싶다면 책을 구매해서 보길 권장합니다!
-
Interface 에 default method 와 static method 가 추가되었다.
default method 는 “default” 라는 키워드를 사용한다.
9.1. 변화하는 API
-
interface 는 library 제공자에게 있어 기능 추가의 hell 이 있었다.
9.1.1. API 버전 1
* 사용자 구현
9.1.2. API 버전 2
* 사용자가 겪는 문제
-
공개된 API 를 고치면 기존 버전과의 호환성 문제가 발생한다.
이런 이유 때문에 공식 자바 컬렉션 API 같은 기존의 API 는 고치기 어렵다.
-
자바 프로그램을 바꾸는 것과 관련된 호환성 문제는 크게 바이너리 호환성, 소스 호환성, 동작 호환성 세 가지로 분류할 수 있다.
뭔가를 바꾼 이후에도 에러 없이 기존 바이너리가 실행될 수 있는 상황을 바이너리 호환성이라고 한다.
예를 들어 인터페이스에 메서드를 추가했을 때 추가된 메서드를 호출하지 않는 한 문제가 일어나지 않는데 이를 바이너리 호환성이라고 한다.
소스 호환성이란 코드를 고쳐도 기존 프로그램을 성공적으로 재컴파일 할 수 있음을 의미한다.
동작 호환성이란 코드를 바꾼 다음에도 같은 입력값이 주어지면 프로그램이 같은 동작을 실행한다는 의미이다.
9.2. 디폴트 메서드란 무엇인가?
-
디폴트 메서드(default method)는 인터페이스를 구현하는 클래스에서 메서드를 구현하지 않을 수 있는 새로운 메서드 시그너처이다.
-
디폴트 메서드는 default 라는 키워드로 시작한다. 그리고 메서드 바디를 포함한다.
-
추상 메서드와 자바 8 의 인터페이스는 다르다.
클래스는 하나의 추상 클래스만 상속받을 수 있지만, 인터페이스는 여러 개 구현할 수 있다.
추상 클래스는 인스턴스 변수(필드)로 공통 상태를 가질 수 있지만, 인터페이스는 인스턴스 변수를 가질 수 없다.
9.3. 디폴트 메서드 활용 패턴
9.3.1. 선택형 메서드
-
디폴트 메서드를 이용하면 Iterator 의 remove 같은 메서드에 기본 구현을 제공할 수 있어 선택적으로 override 할 수 있다.
default void remove(){ throw new UnsupportedOperationException(); }
9.3.2. 동작 다중 상속
* 다중 상속 형식
* 기능이 중복되지 않는 최소의 인터페이스
* 인터페이스 조합
9.4. 해석 규칙
9.4.1. 알아야 할 세 가지 해결 규칙
-
다른 클래스나 인터페이스로부터 같은 시그너처를 갖는 메서드를 상속받을 때는 세 가지 규칙을 따라야 한다.
1. 클래스가 항상 이긴다. 클래스나 슈퍼클래스에서 정의한 메서드가 디폴트 메서드보다 우선권을 갖는다.
2. 1번 규칙 이외의 상황에서는 서브인터페이스가 이긴다. 상속관계를 갖는 인터페이스에서 같은 시그너처를 갖는 메서드를 정의할 때는 서브인터페이스가 이긴다. 즉, B가 A를 상속받는다면 B가 A를 이긴다.
3. 여전히 디폴트 메서드의 우선순위가 결정되지 않았다면 여러 인터페이스를 상속받는 클래스가 명시적으로 디폴트 메서드를 오버라이드하고 호출해야 한다.
9.4.2. 디폴트 메서드를 제공하는 서브인터페이스가 이긴다.
-
public interface A{ … } // hello from A 출력 public interface B extends A { … } // hello from B 출력 public class D implements A { } public class C extends D implements B, A{ public static void main(String… args){ new C().hello(); } }
위의 출력 결과는 hello from B 이다. ( D 가 실제 hello 를 override 하지 않았기 떄문..? )
9.4.3. 충돌 그리고 명시적인 문제 해결
-
충돌이 난 경우에는 컴파일 에러가 난다.
Error: class C inherits unrelated defaults for hello() from types B and A.
* 충돌 해결
-
자바8에서는 X.super.m(…) 형태의 새로운 문법을 제공한다.
여기서 X 는 호출하려는 메서드 m 의 슈퍼인터페이스.
public class C implements B, A{ void hello(){ B.super.hello(); } }
9.4.4. 다이아몬드 문제
-
public interface A{ … } // hello method 가 hello from A 를 출력 public interface B extends A { } public interface C extends A { } public class D implements B, C { public static void main(String… args){ new D().hello(); } }
다이아몬드 문제 (Diamond Problem)이라 부른다.
출력 결과는 hello from A.
-
위의 문제에서 B 만 hello 를 override 하면 B 의 것이 불리고,
B 와 C 모두 hello 를 override 하면 compile error 가 난다.
-
다음 세가지 규칙만 적용하면 모든 충돌 문제를 해결할 수 있다.
1. 클래스가 항상 이긴다. 클래스나 슈퍼 클래스에서 정의한 메서드가 디폴트 메서드보다 우선권을 갖는다.
2. 위 규칙 이외의 상황에서는 서브인터페이스가 이긴다. 상속관계를 갖는 인터페이스에서 같은 시그너처를 갖는 메서드를 정의할 때는 서브인터페이스가 이긴다. 즉 B가 A를 상속받는다면 B가 A를 이긴다.
3. 여전히 디폴트 메서드의 우선순위가 결정되지 않았다면 여러 인터페이스를 상속받는 클래스가 명시적으로 디폴트 메서드를 오버라이드하고 호출해야 한다.
9.5. 요약
-
자바 8의 인터페이스는 구현 코드를 포함하는 디폴트 메서드, 정적 메서드를 정의할 수 있다.
-
디폴트 메서드의 정의는 default 키워드로 시작하며 일반 클래스 메서드처럼 바디를 갖는다.
-
공개된 인터페이스에 추상 메서드를 추가하면 소스 호환성이 깨진다.
-
디폴트 메서드 덕분에 라이브러리 설계자가 API 를 바꿔도 기존 버전과 호환성을 유지할 수 있다.
-
선택형 메서드와 동작 다중 상속에도 디폴트 메서드를 사용할 수 있다.
-
클래스가 같은 시그너처를 갖는 여러 디폴트 메서드를 상속하면서 생기는 충돌 문제를 해결하는 규칙이 있다.
-
클래스나 슈퍼클래스에 정의된 메서드가 다른 디폴틈 ㅔ서드 정의보다 우선한다. 이 외의 상황에서는 서브인터페이스에서 제공하는 디폴트 메서드가 선택된다.
-
두 메서드의 시그너처가 같고, 상속관계로도 충돌 문제를 해결할 수 없을 때는 디폴트 메서드를 사용하는 클래스에서 메서드를 오버라이드해서 어떤 디폴트 메서드를 호출할지 명시적으로 결정해야 한다.
'프로그래밍 놀이터 > 안드로이드, Java' 카테고리의 다른 글
[Java8 In Action] #11 CompletableFuture: 조합할 수 있는 비동기 프로그래밍 (0) | 2018.12.31 |
---|---|
[Java8 In Action] #10 null 대신 Optional (0) | 2018.12.30 |
[Java8 In Action] #8 리팩토링, 테스팅, 디버깅 (0) | 2018.12.28 |
[Java8 In Action] #7 병렬 데이터 처리와 성능 (0) | 2018.12.27 |
[Java8 In Action] #6 스트림으로 데이터 수집 (0) | 2018.12.26 |
댓글