Language/Java

[Java] 자바에서의 다중상속과 인터페이스의 Default Method

Deveun 2021. 9. 18. 08:48
Java는 다중상속을 허용하지 않는다.

 

자바를 사용하면서 당연하게 생각하는 규칙인데, 그렇다면 그 이유가 무엇일까?

그리고 정말로 자바에서 다중상속이 불가능한지 알아보자.

 

다이아몬드 문제 (Diamond Problem)

 

출처: https://youngjinmo.github.io/2021/03/diamond-problem/

 

다음과 같은 구조에서 다중상속을 허용한다고 가정해보자.

Father, Mother 클래스는 Parent 클래스의 추상메소드 funcA() 를 각각 구현한다.

Child 클래스가 Father, Mother 클래스를 둘 다 extends 한다면, Child.funcA() 는 어떤 메소드를 상속받아야할지 알 수 없다. ==> 다중상속의 모호성으로 이를 금지한다.

 

인터페이스를 사용한 다중상속 (Default Method)

클래스의 상속과 반대로 인터페이스는 여러 개를 implements 할 수 있다.

인터페이스는 선언만을 해놓기 때문에 자식 클래스에서 메소드를 오버라이드함으로써 에러가 발생하지 않는다.

 

그런데, Java8부터 인터페이스에 Default Method라는 것이 등장하며, 인터페이스에서도 메소드의 구현이 가능하게 되었다. 이는 앞서 살펴본 다중상속의 문제점을 발생시킬 수도 있는데, 그렇다면 왜 Default Method가 생겨난 것일까? 자바의 신 2권에서는 이렇게 이유를 설명하고 있다.

... "하위 호환성" 때문이다. 예를 들어 설명하자면, 여러분들이 만약 오픈 소스코드를 만들었다고 가정하자. 그 오픈소스가 엄청 유명해져서 전 세계 사람들이 다 사용하고 있는데, 인터페이스에 새로운 메소드를 만들어야 하는 상황이 발생했다. 자칫 잘못하면 내가 만든 오픈소스를 사용한 사람들은 전부 오류가 발생하고 수정을 해야 하는 일이 발생할 수도 있다. 이럴 때 사용하는 것이 바로 default 메소드다. 

 

default로 구현된 메소드를 가진 여러개의 인터페이스를 implements하면 다이아몬드 문제와 동일한 문제가 발생하는데, 이 때 다중상속의 모호성 문제를 해결하기 위해서는 자식 클래스에서 반드시 해당 메서드를 Override 해야한다.  

interface Mother {
    void funcA();
    default void funcB() { // default를 사용하여 인터페이스에서 메소드 구현
        System.out.println("I'm a mother");
    }
    default void funcM() { // default를 사용하여 인터페이스에서 메소드 구현
        System.out.println("mother method");
    }
}

interface Father {
    void funcA();
    default void funcB() { // default를 사용하여 인터페이스에서 메소드 구현
        System.out.println("I'm a father");
    }
    default void funcF() { // default를 사용하여 인터페이스에서 메소드 구현
        System.out.println("father method");
    }
}

class Child implements Mother, Father {

    @Override
    public void funcA() {
        System.out.println("Hello World!");
    }

    @Override
    public void funcB() { // 오버라이드 or 어떤 super 메소드 구현을 사용할지 명시.
        //Mother.super.funcB();
        //Father.super.funcB();
        System.out.println("I'm a child");
    }
}

public class TestMain {
    public static void main(String[] args) {
        Child child = new Child();
        child.funcA();
        child.funcB();
        child.funcM(); // Child 클래스에서 메소드를 구현하지 않고 사용 가능
        child.funcF(); // Child 클래스에서 메소드를 구현하지 않고 사용 가능
    }
}

 

추상클래스와 인터페이스의 차이가 무엇일까?

Default Method를 사용함으로써 인터페이스는 메소드를 선언뿐만 아니라 구현까지 가능하게 되었고, 추상클래스는 불가능한 다중 상속까지 지원을 하니 이러한 궁금증이 생겼다. 추상클래스를 사용할 필요없이 전부 인터페이스를 implements하는 구조로 구현하면 되는것이 아닌가? 하지만 클래스와 인터페이스는 다음과 같은 차이점이 존재한다.

 

o 인터페이스는 public 접근제한자만 가능하다.

o 인터페이스는 생성자를 가질 수 없다.

 

 

 

[참고]

https://youngjinmo.github.io/2021/03/diamond-problem/

https://siyoon210.tistory.com/95

https://wedul.site/320