상속(Inheritance)은 두 클래스 간의 관계로, 한 클래스가 다른 클래스의 속성과 기능을 물려받는 것을 의미합니다. 이 관계는 extends 키워드를 사용해 다음과 같이 정의할 수 있습니다.
public class A extends B { }속성을 상속받는 클래스를 서브 클래스(sub class) 또는 자식 클래스라고 하며, 속성을 제공하는 클래스를 슈퍼 클래스(super class) 또는 부모 클래스라고 합니다.
상속이 이루어지면 슈퍼 클래스의 멤버들이 서브 클래스 객체 안에 복사됩니다. 따라서 서브 클래스의 객체를 통해 부모와 자식 양쪽 클래스의 멤버에 모두 접근할 수 있습니다.
다중 상속(Multiple Inheritance)은 하나의 클래스가 여러 클래스의 속성을 동시에 상속받는 구조를 말합니다. 즉, 하나의 자식 클래스가 여러 개의 부모 클래스를 가질 수 있는 형태입니다. 그러나 Java는 클래스 간의 다중 상속을 지원하지 않습니다.
다이아몬드 문제란?
- 만약 Java가 다중 상속을 지원한다고 가정해 봅시다.
- 추상 메소드 demo()를 가진 추상 클래스 Sample이 존재한다고 합시다.
- 같은 패키지에 있는 두 개의 클래스가 이 클래스를 상속받아 각각 추상 메소드 demo()를 구현합니다.
- 상속의 기본 규칙에 따르면 두 개의 demo() 메소드 복사본이 서브 클래스 객체에 생성되어야 하므로, 서브 클래스는 이름과 매개변수가 완전히 동일한 두 개의 메소드를 갖게 됩니다.
- 이 상태에서 서브 클래스의 객체로 demo() 메소드를 호출하면, 컴파일러는 어느 쪽 메소드를 호출해야 할지 판단할 수 없는 모호한 상황에 빠집니다. 이것이 바로 Java에서 말하는 다이아몬드 문제(diamond problem)입니다. 클래스 상속 구조가 마름모(다이아몬드) 형태가 되기 때문에 이런 이름이 붙었습니다.
다이아몬드 문제의 해결 방법
Java에서는 인터페이스와 디폴트 메소드(default method)를 활용하면 사실상 다중 상속의 효과를 얻을 수 있습니다.
Java 8부터 인터페이스에 디폴트 메소드가 도입되었습니다. 일반 추상 메소드와 달리 디폴트 메소드는 인터페이스 안에 기본 구현(body)을 가질 수 있는 메소드입니다. 따라서 인터페이스에 디폴트 메소드가 있다면, 해당 인터페이스를 구현하는 클래스에서 반드시 오버라이드할 필요가 없습니다.
또한 서로 다른 두 인터페이스에 동일한 이름과 시그니처를 가진 디폴트 메소드를 정의하고, 하나의 클래스에서 이 두 인터페이스를 동시에 구현하는 것도 가능합니다.
단, 이 경우에는 반드시 해당 클래스에서 디폴트 메소드를 오버라이드해야 하며, 메소드 내부에서 인터페이스명.super.메소드명() 형식으로 어떤 인터페이스의 구현을 호출할지 명시적으로 지정해 주어야 합니다. 이렇게 하면 컴파일러의 모호성 문제 없이 다중 상속과 유사한 구조를 안전하게 구현할 수 있습니다.
예제 코드
interface MyInterface1 {
public static int num = 100;
public default void display() {
System.out.println("display method of MyInterface1");
}
}
interface MyInterface2 {
public static int num = 1000;
public default void display() {
System.out.println("display method of MyInterface2");
}
}
public class InterfaceExample implements MyInterface1, MyInterface2 {
public void display() {
MyInterface1.super.display();
// 또는,
MyInterface2.super.display();
}
public static void main(String args[]) {
InterfaceExample obj = new InterfaceExample();
obj.display();
}
}실행 결과
display method of MyInterface1 display method of MyInterface2