Java 를 공부하던중 , 추상클래스와 인터페이스 이 두개를 굳이 나눠야 했나? 라는 의문점이 들어 정리를 하게 되었다.
추상클래스도 크게 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 받은 모든 클래스가 그 기능을 사용 할 수 있다.