java에서는 다중상속을 허용하지않기 때문에 일어날 수 없는 현상이지만
질문에 대한 답을 하지 못해서 포스팅을 하기로했다.
자바8부터는 인터페이스에 default메서드를 정의할 수 있게 되어서, 자바에서도 다중 인터페이스 상속 시에
다이아몬드 상속문제가 발생합니다! (맨 하단에 정리하겠습니다.)
Diamond of Death (다이아몬드 상속 문제)
다중 상속을 지원하게 되면 하나의 클래스가 여러 상위 클래스를 상속받을 수 있다. 이런 특징 때문에 발생하게 되는 문제가 바로 '다이아몬드 문제'이다.
* Parent와 uncle클래스는 GrandParent라는 부모 클래스를 상속 받은 자식 클래스이다.
* 그런데 child라는 클래스는 Parent와 uncle클래스를 동시에 상속받았다.
* 결국 child는 Parent와 uncle를 상속 받기는 했지만, GrandParent클래스를 두 번이나 상속 받는 일이 발생.
* Parent와 uncle에서 GrandParent의 메소드를 오버라이딩했다면 child에서는 어떤 부모클래스의 메소드를 사용해야하는 것일까?에서 충돌발생.
class GrandParent {
void myMethod(){
System.out.println("GrandParent");
}
}
class Parent extends GrandParent {
@Override
void myMethod(){
System.out.println("Parent");
}
}
class uncle extends GrandParent {
@Override
void myMethod(){
System.out.println("uncle");
}
}
class child extends Parent, uncle{
@Override
void myMethod() {
super.myMethod(); //Parent 출력해야 할까? uncle 출력해야 할까?
}
}
C++에서는 위와같이 다중 상속이 허용되어서 다이아몬드 상속문제가 종종 발생하게 되었는데
이러한 모호성을 없애고자 Java는 물려받지 않았다고 한다.
하지만 단일 상속은 클래스를 경직되게 만들고 유연한 구현이 불가하다.
자바에서는 이러한 문제를 Interface와 Composition Pattern을 사용해서 해결할 수 있다.
구현에 대한 책임을 implements하는 클래스에 위임함으로써 다이아몬드문제를 해결할 수 있다.
-----------------------------------------------(추가된 내용)----------------------------------------------------
문제점
Java 8에서 Default Method가 추가되면서 Interface를 구현하는 클래스에서
동일한 Signature를 갖는 n개의 Default Method를 상속받는 상황이 발생할 수 있다.
아래의 예시를 참고하자.
public interface A {
default void hello() {
System.out.println("Hello From A");
}
}
public interface B extends A {
default void hello() {
System.out.println("Hello From B");
}
}
public class D implements A {
public void hello() {
System.out.println("Hello From D");
}
}
public class C implements A, B {
public static void main(String[] args) {
new C().hello(); // 출력 : Hello From B
}
}
//참고: https://goodgid.github.io/Java-8-Default-Method/
Java8에서는 이 문제에 대한 해결책은 세가지규칙을 따른다.
1. 클래스가 항상 이긴다.
클래스나 슈퍼클래스에서 정의한 메소드가 Default Method보다 우선권을 가진다.
만약 예시의 class D에 hello메소드를 구현하지않았다고 가정한다면
D는 Interface A의 Default Method 구현을 상속받게 되며 1번 규칙은 적용되지 않는다.
2. 1번의 규칙 이외의 상황에서는 Sub Interface가 이긴다.
상속관계를 갖는 Interface에서 같은 SIgnature를 갖는 메소드가 정의될 경우
서브 인터페이스가 이긴다.
즉, B가 A를 상속받는다면 B가 A를 이긴다.
3. 그 밖에는 명시적 호출을 한다.
Default 메소드의 우선순위가 결정되지 않았다면 여러 Interface를 상속받는 클래스가
명시적으로 Default 메소드를 Override하고 호출한다.
public interface A {
default void hello() {
System.out.println("Hello From A");
}
}
public interface B {
default void hello() {
System.out.println("Hello From B");
}
}
public class C implements A, B {
public static void main(String[] args) {
new C().hello();
}
}
// 출력
// java: class C inherits unrelated defaults for hello() from types A and B
그래서 위와 같은 문제를 해결하기 위해서 Java8에서는 아래와 같이 X.super.method(...)형태로 새로운 문법을 제공한다.
public class C implements A, B {
public void hello(){
// 명시적으로 Interface B의 Method를 선택한다.
B.super.hello(); // 출력 : Hello From B
}
public static void main(String[] args) {
new C().hello();
}
}
'언어 > JAVA' 카테고리의 다른 글
Adapter Pattern (0) | 2022.01.21 |
---|---|
[UML]클래스 다이어그램 (0) | 2022.01.19 |
디자인패턴 (0) | 2022.01.15 |
OOP의 4대 특성 및 5대 원칙 (0) | 2022.01.14 |
람다식(Lambda Expression) (0) | 2021.12.17 |