다이아몬드 문제(The Diamond Problem)
- 최상위 부모 클래스 A가 있음
- 두 개의 자식 클래스 B, C가 모두 A를 상속받음
- B와 C는 A의 특정 메서드를 각자 **재정의(Override)**함
- 또 다른 자식 클래스 D가 B와 C를 동시에 상속받으려고 함

위와 같이 탈것이라는 클래스를 상속받아 전차(Chariot) 보트(Boat)를 만들었습니다.
이 두 클래스를 상속받아 수륙양용 전차(Amphibiouse Chariot)이라는 클래스를 만들었을 때,
Chariot, Boat는 서로 다른 fillFuel() 메서드를 오버라이딩 했기 때문에 수륙양용 전차 클래스에서는 두 메서드 중 어느 것을 상속받아야 할지 모르게 됩니다.
해결책
자바에서는 이 문제를 해결하기 위해 클래스는 단일 상속만 허용하고, 인터페이스를 여러 개 구현해 마치 다중 상속인 것처럼 구현하도록 했습니다.
구현이 없는 인터페이스
// 만약 자바가 다중 상속을 허용한다면... (가상 코드)
class Chariot extends Vehicle {
@Override
public void fillFuel() {
System.out.println("마차에 건초를 공급합니다.");
}
}
class Boat extends Vehicle {
@Override
public void fillFuel() {
System.out.println("보트에 돛을 답니다."); // 비유적 표현
}
}
class AmphibiousChariot extends Chariot, Boat { // 다중 상속 (가상)
public void refuel() {
// 컴파일러의 딜레마: Chariot의 fillFuel()? Boat의 fillFuel()?
// 어떤 것을 상속받아야 할지 결정할 수 없다!
fillFuel();
}
}
- 인터페이스는 ‘선언’만 있고 ‘구현’은 없습니다. 따라서 여러 인터페이스를 implements 하더라도 어떤 부모의 인터페이스를 구현해야 할지 고민할 필요가 없이 메서드를 오버라이드 하면 됩니다.
default 메서드의 경우
interface HasGasoline {
default void fillFuel() {
System.out.println("주유소에서 휘발유를 채웁니다.");
}
}
interface HasElectricity {
default void fillFuel() {
System.out.println("충전소에서 전기를 충전합니다.");
}
}
// 두 인터페이스를 구현하면 컴파일 에러 발생!
// "Duplicate default methods ... inherit unrelated defaults"
class HybridCar implements HasGasoline, HasElectricity {
// 해결책: 개발자가 직접 오버라이드하여 모호성을 제거해야만 한다.
@Override
public void fillFuel() {
// 개발자의 의도에 따라 한쪽을 선택하거나 새로운 구현을 제공
HasGasoline.super.fillFuel();
HasElectricity.super.fillFuel();
System.out.println("하이브리드 자동차의 연료를 모두 채웁니다.");
}
}
- Java 8부터 인터페이스에 default 메서드가 추가되어 인터페이스 내부에 구현된 메서드를 가질 수 있습니다. 이럴 경우 서로 다른 인터페이스에서 같은 default를 상속받을 수 있게 되는데, 자바에서는 이를 구현 클래스가 해당 메서드를 반드시 직접 오버라이드 하도록 강제하여 모호성을 해결합니다.
결론
단순함과 명확성
결론적으로, 자바는 다중 상속이 제공하는 유연함보다, 그로 인해 발생할 수 있는 예측 불가능성과 복잡성이 시스템에 더 안 좋다고 판단했습니다. 인터페이스를 통해 다형성이라는 장점은 취하면서도, 구현의 모호함은 차단한 것입니다.
이로 인해 단순함, 명확성 또 안정성을 가질 수 있기 때문에 자바는 다중상속을 지원하지 않습니다.
'CS > JAVA' 카테고리의 다른 글
| [JAVA] Override, Overriding (1) | 2025.07.18 |
|---|