추상클래스 와 인터페이스 , 다이아몬드 문제

정지원·2022년 4월 19일
1

Java 를 공부하던중 , 추상클래스와 인터페이스 이 두개를 굳이 나눠야 했나? 라는 의문점이 들어 정리를 하게 되었다.

추상클래스

추상클래스도 크게 2가지로 나뉠수 있다.

  1. 추상 메소드를 가진 추상클래스
  2. 추상 메소드가 없는 추상 클래스

대부분의 인터넷에 나와있는 추상클래스의 정의를 보면 "하나이상의 추상메소드를 포함하는 클래스" 라고 되어있는데 , 나는 잘 공감하지 못하겠다.

왜냐하면, 추상메소드가 없어도 추상클래스를 구현할 수 있기 때문이다. (abstract 키워드를 통해)

내가 생각하는 추상클래스의 정의는 객체설계를 설계할때, 추상화 작업 즉 객체들의 공통부분을 추출하는 부분에서
다형성을 구현할 수 있는 방식 중 하나이다. 라고 생각한다.

추상 메소드는 다들 알다시피, 메소드의 body 부분 없이 원형만 선언 된 메소드 이다.
추상 클래스 자체를 인스턴스화 시키지 못하는 이유가 여기에 있다.

추상 클래스는 추상 메소드를 가질 수 있는데 , 이를 인스턴스화 시킬수 있다면 원형만 선언 된 추상 메소드를 호출 할 수 있어지기 때문이다.

물론 일반 메소드나 필드를 가질 수도 있기 때문에 상속 받은 클래스는 특정 공통된 기능을 상속받아 사용할 수 있고 ,공통된 속성이지만 서로 다른 행위를 일때에는 상속 받은 메소드를 오버라이딩 할 수 있다.

<장점>
부모클래스에서 공통 부분을 구현과 설계가 완료되면 자식 클래스에서 상속받아 기능을 확장 시 이롭다.
자식 클래스에서 추상메서드를 반드시 구현하도록 강요한다. 이는 프로그램의 표준화 정도를 높인다.
공통 사항이 한곳에서 관리되어 개발 및 유지보수에 용이하다.

인터페이스

인터페이스는 일반 메소드 , 필드를 가질 수 없다.
추상메소드 또는 public static final 필드 (상수) 만 가질 수 있다.

하지만 java8 이상부터는 인터페이스에서 default 메소드 , static 메소드를 구현 할 수 있게 되었다.

하지만 본질적으로 인터페이스의 목적은 어떠한 기능을 구현하게끔 강제 하는 목적이 있다.

인터페이스와 상속의 차이점을 명확히 보여주는 예시가 있어 가지고 왔다.
상속 : is kind of
인터페이스 : is able to

동물과 사자는 is kind of 상속의 관계이다.
즉 동물들의 모든 특징을 사자가 가지고 있지 않아도 된다라는 것이다.

하지만 스마트폰과 갤럭시22 는 is able to 의 관계이다.
우리가 비싼 돈을 주고 갤럭시 22를 구매를 하였는데 , 전화가 안된다거나 , 어플을 다운받을 수 없다거나 기능이 한가지라도 되지 않는다면 그것은 고장난 폰이거나 스마트폰이라고 할수 없을 것이다.

정리하면, 상속을 통해 우리는 부모의 기능들을 자식이 사용 또는 재정의 할 수 있게 함으로 하위클래스로의 확장의 포인트를 가지고 있다면

인터페이스는 정의한 메소드를 구현하게끔 강제 하여 implements 한 클래스에 목적에 맞게 기능(메소드)를 구현해라에 포인트를 가지고 있다.

더하여 인터페이스는 추상클래스에서 할 수 없는 다중 상속 기능을 제공한다.

다중상속(extends) 를 하지 못하는 이유는 바로 다이아몬드 문제 때문이다.

다이아몬드 문제

예를 들어 A 라는 클래스에 print 라는 메소드가 있고 ,B 라는 메소드에도 print 메소드가 있다고 가정해보자.

만약 C 라는 클래스가 A,B 를 상속받았을때, super.print() 를 호출했을 경우 즉 부모에서 정의된 메서드를 호출 했을 경우 Aㅢ print() 가 실행되야 할까, 아니면 B의 print() 가 실행되야 할까?

바로 이러한 이유때문에 자바에서는 다중 상속이 불가능하다.

하지만 인터페이스를 보면 인터페이스에서는 default 메서드를 사용하지 않는다고 가정 했을때 , 모두 각 클래스에 목적에 따라 메서드의 구현부가 달라지기 때문에 다이아몬드 문제가 발생 하지 않는다.

즉 super 키워드 같이 부모에게 메서드를 받아오지 않고 선언부만 같지 각 클래스의 추상메서드 정의부는 물려받는것이 아닌 새로 정의 하는 개념이기 때문이다.

하지만 인터페이스도 default 메서드를 구현하게 되면 다중상속이 되지 않는다.

default 메서드는 인터페이스를 구현한 모든 클래스가 정의 없이 사용 할 수 있는 메서드 이다.

그렇기에 다이아몬드 문제가 발생되어 다중상속을 할 수 없는 것이다.

그럼에도 불구하고 default 메서드가 주는 이점은 확실하다.

전의 스마트폰 예제를 들고 와서 , 스마트폰에 홍채 인식이라는 기능이 추가되었다고 가정해보자.

물론 새로운 클래스에(새로운 기종) 따로 홍채인식이라는 메서드를 추가 할 수 있지만 스마트폰을 implements 받은 모든 클래스들이 이 홍채인식을 사용 하게 끔 만들어야 하는 가정을 세운다.

한가지 방법으로는 interface 에 추상메서드 홍채인식을 정의를 하여 각 클래스 마다 정의를 해준다.

하지만 implements 받은 클래스가 지금까지 나온 스마트폰들을 다 일일이 재정의 해준다? 생각만 해도 끔찍하지 않을 수 없다.

이러한 이미 운영중인 서비스에서 또는 구현 객체들이 굉장히 많고 일일이 수정해주기 쉽지 않은 상황일때 , 인터페이스의 default 메서드를 선언하고 정의해준다면 implements 받은 모든 클래스가 그 기능을 사용 할 수 있다.

profile
지속적인 발전, 태도

0개의 댓글