[Java]클래스와 인터페이스의 단일 상속과 다중 상속

Euiyeon Park·2024년 9월 10일

Java

목록 보기
12/16
post-thumbnail

🎂 상속(Inheritance)

상속은 객체 지향 프로그래밍의 중요한 개념 중 하나로,
기존 클래스(부모 클래스)의 특성과 기능을 새로운 클래스(자식 클래스)가 물려받아 재사용하거나 확장하는 것을 의미한다.

상속은 코드의 재사용성을 높이고, 유지 보수를 쉽게 하며, 객체 간의 계층 구조를 형성할 수 있게한다.
또한 상속을 통해 객체의 타입을 부모 클래스 타입으로 다룰 수 있어,
하나의 객체가 여러 가지 형태를 가질 수 있는 다형성을 지원한다.

🎂 클래스의 단일 상속(Single Inheritance)

클래스는 오직 하나의 부모 클래스로부터만 상속을 받을 수 있다.

특징

  1. 부모 클래스의 필드와 메소드를 자식 클래스에서 상속받아 사용한다. - 재사용
  2. 상속 구조가 단순해지며, 부모와 자식 간의 명확한 관계를 형성한다.
  3. 다중 상속의 복잡한 상속 구조나 모호성 문제를 단일 상속으로 피할 수 있다.

🪄 다이아몬드 문제(Diamond Problem)

다이아몬드 문제는 객체 지향 프로그램에서 다중 상속을 지원할 때 발생할 수 있는
상속의 모호성 문제를 의미한다.

자식 클래스가 여러 부모 클래스로부터 상속을 받을 때
동일한 이름과 시그니처의 메서드 또는 속성이 여러 부모 클래스에 존재하는 경우
자식 클래스가 어느 부모의 메서드나 속성을 상속받아야할 지 모호해지는 상황에서 발생한다.

🎂 인터페이스의 다중 상속(Multiple Inheritance)

클래스는 여러 개의 인터페이스를 구현(implements) 할 수 있으며, 이를 다중 상속이라고 한다.
인터페이스는 메소드의 시그니처(선언)만을 정의하고, 실제 구현은 클래스에서 제공한다.

특징

  1. 인터페이스에 정의된 메소드(추상 메소드)는 반드시 구현 클래스에서 정의해야 한다.
  2. 여러 개의 인터페이스를 구현함으로써 다양한 기능을 한 클래스에 통합할 수 있다.
  3. 클래스가 여러 기능을 구현해야할 때 다중 상속이 가능한 인터페이스를 이용한다.
  4. 충돌 해결이 가능하다.

🪄 디폴트 메서드(Default Method)

디폴트 메서드는 인터페이스 내에서 구현부가 있는 메서드로,
인터페이스를 구현하는 클래스가 해당 메서드를 명시적으로 재정의하지 않으면
디폴트 메서드가 제공하는 기본 구현을 사용하게 된다.

인터페이스 내 메소드에 default 키워드를 사용해 구현부{}를 정의한다.

public interface MyInterface{
	// 추상 메서드
    void myMethod();
    
    // 디폴트 메서드
    deafault void myDefaultMethod(){
    	System.out.println("This is a default method.");
    }
}

디폴트 메서드는 상속된 클래스나 인터페이스에서 재정의(Override)가 가능하다.

🎂 인터페이스 다중 상속 충돌 해결

예를 들어, Class AInterface A, Interface C를 구현했을 때(implements),
A, B동일한 메소드 시그니처가 있는 상황을 가정해보자.

※ 메소드 시그니처(Method Signature)란 메소드 정의에서 메소드 이름과 파라미터 목록을 의미한다.

public interface InterfaceA{
	void myMethod();
}
public interface InterfaceB{
	void myMethod();
}

1. 디폴트 메서드가 없는 경우

인터페이스 A, B가 동일한 메서드 시그니처를 가지고 있지만
Class AmyMethod() 하나만 구현하면된다.

public class ClassA{
	@Override
    public void myMethod(){
    	System.out.println("This is a my method.");
    }
}

2. 디폴트 메서드가 있는 경우

인터페이스 A, B동일한 디폴트 메서드가 있는 경우 충돌이 발생할 수 있다.
구현 클래스에서 충돌을 해결하려면, 해당 메서드를 명시적으로 오버라이딩
어느 부모 인터페이스의 메서드를 사용할지 지정해야 한다.

public interface InterfaceA{
	default void myMethod(){
    	System.out.println("This is a B's default method.");
    }
}
public interface InterfaceB{
	default void myMethod(){
    	System.out.println("This is a C's default method.");
    }
}

2-1. 부모 인터페이스의 메서드 지정

구현 클래스 ClassAmyMethod()메서드를 오버라이딩하고,
A.super.myMethod()과 같이 특정 인터페이스default 메서드를 명시적으로 호출

public class ClassA implements InterfaceA, InterfaceB{
	@Override
    public void myMethod(){
    	// 둘 다 호출
        A.super.myMethod();
        B.super.myMethod();
        
        // 또는 하나만 호출
        A.super.myMethod();
    }
}

2-2. 완전히 새로운 구현

충돌하는 메서드 myMethod() 대신 완전히 새로운 구현을 제공해 두 인터페이스의 충돌을 해결한다.
Class AABdefault 메소드를 사용하지 않고, 자신만의 방식으로 새롭게 구현한다.

public class ClassA implements InterfaceA, InterfaceB{
	@Override
    public void myMethod(){
    	System.out.println("C's own implementation of myMethod");
    }
}

🪄 인터페이스 상속

인터페이스도 다른 인터페이스를 상속할 수 있으며, 이 경우에도 다중 상속이 가능하다.

public interface InterfaceA{
	void myMethodA();
}

public interface InterfaceB{
	void myMethodB();
}

public interface InterfaceC extends InterfaceA, InterfaceB{
	void myMethodC();
}

하위 인터페이스(InterfaceC)를 구현하는 클래스는 하위 인터페이스의 메서드뿐만 아니라
상위 인터페이스(A,B)의 모든 추상 메소드를 구현해야 한다.
그렇기 때문에 구현 클래스로부터 객체를 생성한 뒤 상위/하위 인터페이스 타입으로 변환이 가능하다.


정리

  • 클래스는 다중 상속의 모호성 문제, 상속 구조 복잡성 증가로 인해 단일 상속을 원칙으로 한다.
  • 인터페이스는 메서드 시그니처만 제공하며, 구현체를 제공하지 않기 때문에 다중 상속이 가능하다.
    • 각 인터페이스에서 메서드 구현이 제공되지 않아 다중 상속을해도 충돌이 발생하지 않는다.
  • 디폴드 메서드 도입으로 충돌 발생 가능성이 있지만, 충돌을 해결할 수 있는 매커니즘을 제공한다.
    • 구현 클래스에서 메서드 오버라이딩을 통해 충돌을 해결한다.
    • super 키워드를 사용해 특정 인터페이스의 메소드를 명시적으로 호출한다.

ref

velog.io/@heoseungyeon

profile
"개발자는 해결사이자 발견자이다✨" - Michael C. Feathers

0개의 댓글