디폴트 메소드와 다중상속
JAVA8 부터 등장한 디폴트 메소드에 의해 interface 의 유연함은 좋아졌고, 그 말은 즉 변경에 대한 호환성이 좋아졌다는 의미로 생각할 수 있을 것 같습니다.
디폴트 메소드에 대한 이야기는 이 포스팅에서 참고! :-)
하지만 interface 에 행위를 추가함에 있어서 JAVA에서 등장하지 않았던 다중상속과 같은 기능이 생겼으며, 어떤 면에서는 편리하지만 이에 따른 충돌이 있음을 생각해 볼 수 있습니다.
아래와 같이 말이죠.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 | /** * 비행을 할 수 있는 인터페이스 명시. * * @author Doohyun * */ public interface IFlyable { /** * 날 수 있는 방법 명시. */ void fly(); /** * 음식으로 변환 * @return */ default Object toFoodObject() { return new FoodByFlyAble(); } } public interface IRunable { /** * 수 있는 방법 명시. */ void run(); /** * 음식으로 변환 * @return */ default Object toFoodObject() { return new FoodByFRunAble(); } } /** * 닭 클래스 정의 * * @author Doohyun * */ public class Chicken implements IFlyable, IRunable{ @Override public void fly() { System.out.println("30 m 점프"); } @Override public void run() { System.out.println("열심히 뛰"); } } | cs |
Chicken 클래스가 toFoodObject() 를 사용할 때, 어떤 interface 의 디폴트 메소드를 사용해야하는 것일까요? ㅡㅡ^
물론 이러한 모호성을 없애기 위해 JAVA8 in Action 에서는 해석 규칙을 명시하였습니다.
1. 클래스에서 디폴트 메소드를 재정의한다면, 클래스가 무조건 승리!
Chicken 클래스에서 아래와 같이 toFoodObject() 을 재정의 한다면, 일단 문제는 사라집니다.
또한 interface 는 super 클래스가 아니니, super 키워드로 toFoodObject() 을 호출하는 모호성은 없다고 볼 수 있습니다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | public class Chicken implements IFlyable, IRunable{ @Override public void fly() { System.out.println("30 m 점프"); } @Override public void run() { System.out.println("열심히 뛰"); } /** * toFoodObject 을 재정의!! */ @Override public Object toFoodObject() { return new FrenchStyleChiken(); } } | cs |
2. Sub interface 가 디폴트 메소드 재정의한다면, sub interface 가 승리!
interface 간 상속구조가 존재하고, sub interface 가 부모 interface 의 디폴트 메소드를 재정의한다면, 아래와 같이 sub interface 디폴트 메소드가 실행됩니다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 | /** * 비행을 할 수 있는 인터페이스 명시. * * @author Doohyun * */ interface IFlyable { /** * 날 수 있는 방법 명시. */ void fly(); /** * 음식으로 변환 * @return */ default Object toFoodObject() { return new FoodByFlyAble(); } } interface IRunable extends IFlyable{ /** * 수 있는 방법 명시. */ void run(); /** * 음식으로 변환 * @return */ @Override default Object toFoodObject() { return new FoodByFRunAble(); } } /** * 닭 클래스 정의 * * @author Doohyun * */ class Chicken implements IRunable{ @Override public void fly() { System.out.println("30 m 점프"); } @Override public void run() { System.out.println("열심히 뛰"); } } System.out.println(chicken.toString()); // Return // FoodByRunnable | cs |
3. 디폴트 메소드의 우선순위가 결정되지 않았다면, 명시적 선언!
1, 2 번 규칙에 의해서 여전히 디폴트 메소드의 우선순위가 결정되지 않았다면 상속대상이 되는 클래스에서 어떤 메소드를 사용할지 명시적으로 선언해야 합니다.
JAVA8 에서는 인터페이스의 디폴트 메소드를 명시적으로 선언하기 위해서,
InterfaceName.super.method(...)
형태의 문법을 제공합니다. 사용법은 아래와 같습니다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | /* * 닭 클래스 정의 * * @author Doohyun * */ public class Chicken implements IRunable, IFlyable{ @Override public void fly() { System.out.println("30 m 점프"); } @Override public void run() { System.out.println("열심히 뛰"); } @Override public Object toFoodObject() { return IRunable.super.toFoodObject(); } } | cs |
디폴트 메소드에 의한 복잡한 상속구조에 의한 충돌은 위 세 가지 규칙만 따르면 해결 가능성이 존재합니다.
디폴트 메소드가 추가됨에 따라 interface 변경에 대한 호환성 뿐만 아니라, 여러 기능을 가진 모듈체(디폴트 메소드에 의한 기능)를 쉽게 붙일 수 있다는 것은 꽤 매력적일 수 있습니다. :-)
|
'개발이야기 > 함수형 방법론' 카테고리의 다른 글
CompleteableFuture 를 이용한 비동기 처리 조합 (0) | 2017.03.24 |
---|---|
CompleteableFuture 를 이용한 비동기 처리 (0) | 2017.03.23 |
Null 대신 Optional! (0) | 2017.03.14 |
JAVA8 에서의 인터페이스 변화 (디폴트 메소드) (2) | 2017.03.10 |
함수형 프로그래밍으로 리팩토링. (0) | 2017.02.21 |