다이아몬드 문제란?
다이아몬드 문제는 객체지향 프로그래밍에서 다중 상속을 지원하는 언어에서 발생할 수 있는 모호성 문제로, 다이아몬드 모양의 상속 계층 구조를 가질 때 발생한다.

위 클래스 다이어그램과 같은 상속 구조에서 D 클래스의 인스턴스가 A 클래스에서 정의된 메서드나 속성에 접근할 때, B를 통해 접근해야 할지 C를 통해 접근해야 할지 모호해지는 문제가 발생한다.
자바에서의 다이아몬드 문제
다이아몬드 문제는 크게 두 가지 이슈를 가진다:
- 메서드 호출의 모호성: D에서 A의 메서드를 호출할 때, B의 구현과 C의 구현 중 어떤 것을 사용해야 할지 모호하다.
- 필드 중복 문제: A에 인스턴스 변수가 있다면, B와 C를 통해 각각 중복 상속되어 D는 동일한 A 필드의 두 복사본을 갖게 된다. 이로 인해, 메모리 낭비가 발생하고, 한 복사본을 변경해도 다른 복사본은 변경되지 않기 때문에 논리적 불일치가 발생한다. 또, 어떤 필드에 접근해야 하는지 모호성이 발생한다.
자바는 클래스의 다중 상속을 지원하지 않는다. 하나의 클래스는 단 하나의 클래스만 상속받을 수 있다. 이는 의도적으로 다이아몬드 문제를 방지하기 위한 설계였다.
// 상위 클래스 A
class A {
// 다이아몬드 문제를 일으킬 수 있는 메서드
public void doSomething() {
System.out.println("A's implementation of doSomething()");
}
}
// A를 상속받는 B 클래스
class B extends A {
// A의 메서드를 오버라이드
@Override
public void doSomething() {
System.out.println("B's implementation of doSomething()");
}
}
// A를 상속받는 C 클래스
class C extends A {
// A의 메서드를 오버라이드
@Override
public void doSomething() {
System.out.println("C's implementation of doSomething()");
}
}
// 만약 자바가 다중 상속을 허용한다면:
// B와 C를 모두 상속받는 D 클래스
class D extends B, C { // 이 문법은 자바에서 실제로는 허용되지 않음
@Override
public void doSomething() {
super.doSomething(); // 여기서도 모호함: B의 super인지 C의 super인지?
}
}
public class DiamondProblemExample {
public static void main(String[] args) {
D d = new D();
// 다이아몬드 문제: D가 doSomething()을 호출할 때, 어떤 doSomething()을 호출해야할지 모호함
// 컴파일러는 어떤 메서드를 호출해야 할지 결정할 수 없음
d.doSomething();
}
}
`d.doSomething()`를 호출할 때 해당 메서드가 두 개의 상위 클래스에서 모두 정의되어 있기 때문에, 어떤 메서드를 실행해야 할지 모호하다. 자바 컴파일러는 이런 모호성을 방지하기 위해 클래스 다중 상속을 허용하지 않는다.
필드 중복 문제도 존재한다.
// 상위 클래스 A
class A {
protected int value = 10; // 인스턴스 변수
}
// A를 상속받는 B와 C 클래스
class B extends A { }
class C extends A { }
// 만약 자바가 다중 상속을 허용한다면:
class D extends B, C { // 자바에서 실제로는 불가능
public void printValues() {
// D는 두 개의 value 필드를 가짐 - B로부터와 C로부터
System.out.println("B's value: " + /* B로부터의 value */);
System.out.println("C's value: " + /* C로부터의 value */);
}
public void updateValue(int newValue) {
// 어느 value를 업데이트해야 할까? B의 것? C의 것?
value = newValue; // 모호함!
}
}
D 클래스는 A 클래스에서 정의된 value 필드의 두 개의 복사본을 갖게 된다. (B를 통해 상속된 복사본, C를 통해 상속된 복사본)
동일한 이름과 목적을 가진 두 개의 value 필드가 D 객체 내에 별도의 메모리 공간에 존재하기 때문에 메모리 낭비가 발생하고, B를 통해 value를 변경해도 C의 value는 영향을 받지 않기 때문에 논리적 불일치가 발생한다.
이런 모호성과 필드의 중복 때문에 자바는 클래스 다중 상속을 허용하지 않는다.
인터페이스 다중 상속
자바에서 클래스는 다중 상속을 허용되지 않았지만, 인터페이스는 다중 상속이 허용된다. 인터페이스는 메서드만 선언하고 클래스가 인터페이스를 구현할 때는 모든 추상 메서드를 직접 구현해야 하기 때문에, 상속 과정에서 발생할 수 있는 모호성은 발생하지 않는다. 하지만 자바 8부터 인터페이스가 기본 메서드(default methods)를 가질 수 있게 되면서, 인터페이스를 통한 다중 상속에서 다이아몬드 문제가 다시 등장하였다.
interface A {
default void hello() {
System.out.println("Hello from A");
}
}
interface B extends A {
default void hello() {
System.out.println("Hello from B");
}
}
interface C extends A {
default void hello() {
System.out.println("Hello from C");
}
}
class D implements B, C {
// D는 어떤 hello()를 상속받아야 할지 모호함
}
위 코드에서 클래스 D는 인터페이스 B와 C를 모두 구현한다. 하지만 B와 C는 모두 동일한 `hello()` 메서드의 기본 구현을 제공한다. 이 경우 D 클래스의 인스턴스가 `hello()` 메서드를 호출할 때 어떤 구현을 사용해야 할지 모호해진다.
자바에서 다이아몬드 문제 해결 방법
자바는 이러한 모호성을 해결하기 위해 명확한 규칙을 제공한다.
1. 충돌하는 메서드를 직접 재정의한다.
class D implements B, C {
@Override
public void hello() {
System.out.println("Hello from D");
}
}
2. super 키워드를 통한 특정 구현 선택한다. 자바 8부터 `super`키워드를 사용해 특정 인터페이스의 기본 메서드를 명시적으로 호출할 수 있다.
class D implements B, C {
@Override
public void hello() {
B.super.hello(); // B의 구현을 사용
// 또는
// C.super.hello(); // C의 구현을 사용
}
}
3. 클래스와 인터페이스가 동일한 메서드를 정의하고 있다면 클래스의 구현을 우선한다.
class X {
public void hello() {
System.out.println("Hello from X");
}
}
interface Y {
default void hello() {
System.out.println("Hello from Y");
}
}
class Z extends X implements Y {
// X의 hello() 메서드가 Y의 기본 구현보다 우선
}
4. 상위 인터페이스보다 하위 인터페이스의 기본 메서드를 우선한다.
interface A {
default void hello() {
System.out.println("Hello from A");
}
}
interface B extends A {
default void hello() {
System.out.println("Hello from B");
}
}
class C implements A, B {
// B가 A를 확장하므로, B의 hello() 구현이 우선
}
'Java' 카테고리의 다른 글
| [Java] PCCP 정기 시험 후기(Java Lv.5) (0) | 2025.12.22 |
|---|---|
| [Java] 직렬화와 역직렬화 (2) | 2025.08.18 |
| [Java] 동일성과 동등성에 관하여 (0) | 2025.03.17 |
| [Java] equals()와 hashCode()에 관하여 (0) | 2025.03.14 |
| [Java] 얕은 복사와 깊은 복사 (4) | 2025.03.11 |