인터페이스 다중 상속과 Default Method

Haechan Kim·2025년 6월 1일
0

Modern Java

목록 보기
7/10

다중 상속

자바는 다중 상속을 허용하지 않는다.

Father와 Mother 클래스가 Person 클래스를 extends 했고, 추상 메서드 funcA를 각각 구현했다.
Child가 Father와 Mother를 둘 다 extends 한다면, Child.funcA()는 어떤 메서드를 상속받아야 할 지 알 수 없다. (다중상속의 모호성)
이를 다이아몬드 문제라고 부르며, 다중상속을 허용하지 않는 이유이다.

하지만 인터페이스의 다중 상속은 허용된다. 한 인터페이스가 여러 인터페이스를 extends 할 수 있고, 클래스는 여러 인터페이스를 implements 할 수 있다.

interface A { ... }

interface B { ... }

interface C extends A, B { ... }

class Class implements A, B { ... }

기존의 인터페이스는 추상 메소드만 정의할 수 있었지만, Java 8부터 default 키워드를 사용해 본문이 있는 메서드 또한 정의할 수 있게 되었다.

default method는 '하위 호환성' 때문에 만들어졌다.

기존 인터페이스에 새로운 메서드를 추가해야 하는 경우, 해당 인터페이스를 구현하는 모든 클래스에 새 메서드를 구현해야 했다. 이는 확장과 호환성과 기존 코드 변경에 큰 문제가 있다.

default method를 사용하면 해당 문제를 해결할 수 있다.

interface A {
	void funcA();
    default void funcB() {
    	System.out.println("This is default method.");
    }
}

이점

(1) 역호환성 유지 : 기존 인터페이스 확장하고 새 메서드 추가할 때, 이미 구현한 클래스는 새 메서드 구현하지 않아도 됨.
(2) 기본 구현 제공
(3) 다중 상속 문제 해결 : default method를 통해 인터페이스에서 다중 상속과 유사한 기능을 제공할 수 있음.

다중 상속 문제 해결

클래스는 다이아몬드 문제 때문에 다중 상속을 허용하지 않는다. 두 상위 클래스에 같은 메서드가 있을 경우, 하위 클래스에서 어떤 메서드를 사용해야 할 지 알 수 없다. (다중 상속의 모호성)

default method 또한 동일한 문제가 발생하는데, 이를 해결하기 위해서는 자식 클래스에서 반드시 해당 메서드를 override 하거나, 어떤 super 메서드를 사용할 지 명시해야 한다.

interface Mother {
    void funcA();
    default void funcB() {
        System.out.println("I'm a mother");
    }
    
    default void funcM() {
        System.out.println("mother method");
    }
}

interface Father {
    void funcA();
    default void funcB() {
        System.out.println("I'm a father");
    }
    
    default void funcF() {
        System.out.println("father method");
    }
}

class Child implements Mother, Father {
    public void funcA() {
        System.out.println("AA");
    }
    
    public void funcB() {
        // System.out.println("default method");
        Mother.super.funcB();
        
    }
}

public class Main
{
	public static void main(String[] args) {
		Child child = new Child();
		child.funcA();
		child.funcB();
		child.funcM();
		child.funcF();
	}
}

클래스의 다중 상속은?

default method를 통해 인터페이스에서 다중 상속을 구현할 때 발생하는 다이아몬드 문제는 해당 메서드를 override 하거나, 어떤 super 메서드를 사용할 지 명시를 통해 해결할 수 있다고 하였다.

그렇다면, 클래스의 다중 상속도 이 방법을 통해 해결할 수 있는 것 아닐까??

default method 에서는 괜찮은 이유

Java 8 에서 default method를 추가할 때 '명시적으로 오버라이딩 해야 한다'는 규칙을 추가함으로써 모호성을 해결 할 수 있는 구조로 설계되었다.

인터페이스는 상태(state)가 없고 논리적 행동(behavior)만을 제공하므로, 충돌 시 컴파일 에러를 내고, 명시적 해결로써 해결이 가능하다.
상태 : 객체 내부에 저장된 데이터 값, 필드(멤버 변수)

하지만 클래스는
(1) 필드(멤버 변수)도 상속이 가능하다.
두 부모 클래스에서 동일한 이름의 필드가 있을 경우 충돌이 가능한다.

(2) 생성자가 충돌할 수 있다.
인터페이스는 생성자를 가질 수 없다.
클래스는 생성자를 가질 수 있으므로 자식 클래스에서 어떤 부모의 생성자 호출할 지 명확하지 않다.

이처럼 클래스는 인터페이스보다 복잡한 상속 구조로 인해 변경이 위험하고, 의도 파악이 어렵다.

즉 자바는 클래스 다중 상속이 야기하는 복잡성과 모호성을 방지하기 위해 이를 금지했다. 반면, 인터페이스는 충돌이 있어도 오버라이딩을 통해 해결할 수 있도록 설계했다.

이로 인해 자바는 단순한 객체 모델을 유지하면서, 인터페이스 다중 상속을 통해 유연한 코드 구조를 만들 수 있게 되었다.


참고

0개의 댓글