자바에서의 다중상속 문제(a.k.a. Diamond Problem)

자바에서는 왜 다중상속을 허용하지 않는걸까? 그리고 어떻게 인터페이스를 통해서는 다중상속이 가능한걸까?

이를 알아보기 위해 아래와 같은 상속관계를 갖는 클래스들을 생성해보겠다.

Child -> Mother -> GrandMother, Father -> GrandMother

Person.java

1
2
3
4
5
6
7
public class Person {
String nation = "대한민국";

protected void 가문(){
System.out.println("진인사대천명");
}
}

Father.java

1
2
3
4
5
6
public class Father extends Person {
@Override
protected void 가문() {
System.out.println("가나다라마바사");
}
}

Mother.java

1
2
3
4
5
6
public class Mother extends Person {
@Override
protected void 가문() {
System.out.println("Hello World.");
}
}

Child.java

1
2
3
4
5
6
7
public class Child extends Mother {
public static void main(String[] args) {
Child child = new Child();
System.out.println("내 국적은 "+child.nation+"입니다.");
child.가문();
}
}

Childmain()를 실행한 결과는 다음과 같다.

1
2
내 국적은 대한민국입니다.
Hello World.

Child 클래스의 인스턴스를 사용해서 가문()nation 필드를 출력했는데, 둘다 Child 클래스에 선언된 필드가 아니다. GrandMother 클래스에 선언된 필드를 Mother 클래스의 상속을 거쳐 Child 까지 상속된것이다.

자바에서 다중상속이 안되는 이유

만약 자바에서 다중상속이 허용되어서 ChildMotherFather를 모두 상속한다고 가정해보자.

위와 같은 상속 관계에서 ChildFatherMother의 멤버 필드와 메소드를 상속받을 것이다. 그런데 FatherMother에 같은 메소드가 있다면 어떨까? FatherMotherPerson을 상속받으면서 가문()을 서로 다르게 오버라이딩했다.

그렇다면 Father와 Mother를 상속받는 Child에서 child.가문()은 어떤걸 출력할까?

이 애플리케이션을 실행해야하는 JVM에서는 Father에도 선언되어 있고, Mother에도 선언되어 있는 가문()중 어떤걸 Child가 상속받아야하는지를 모르기 때문에 컴파일 에러가 발생한다.

그래서 자바에서 다중상속이 안되는 이와 같은 문제를 다이아몬드 문제라고한다.

인터페이스를 통한 다중상속은 가능한 이유

구현체를 갖고있는 메소드 상속이 불가능하다면 구현체가 없는 추상메소드만 있는 인터페이스는 어떨까?

Person.java

1
2
3
4
public interface Person {
String nation = "대한민국";
void 가문();
}

Father.java

1
2
3
4
public interface Father extends {
@Override
void 가문();
}

Mother.java

1
2
3
4
public interface Mother extends Person {
@Override
void 가문();
}

Child.java

1
2
3
4
5
6
7
8
9
10
11
12
13
public class Child implements Father, Mother {
@Override
void 가문(){
System.out.println("Hello World.");
}

public static void main(){
Child child = new Child();

System.out.println("내 국적은 "+child.nation+"입니다.");
child.가문();
}
}

이 코드는 컴파일 에러 없이 실행가능했다. 자바에서 다중상속(extends)은 안되는데, 왜 인터페이스를 통한 다중상속(implements)은 가능한걸까?

상속받은 메소드가 구현체가 없는 추상 메소드이기 때문이다. 어차피 구현체가 없기 때문에 Child가 상속받은 메소드가 Father로부터 받은건지, Mother로부터 받은건지 알필요가 없다. 어차피 상속받은 객체인 Child에서 구현을 할 것이기 때문이다.