추상클래스: "~이다"
인터페이스 : "~을 할 수 있다" ex) Comparable, Iterable
Kebin은 사람이다. 그래서 Human이라는 추상클래스를 상속받아 Human의 자식이 된다.
당연하게도 Human은 Creature를 상속받기 때문에 Kebin은 Creature이기도 하다. 하지만 절대 Kebin이 Animal은 될 수 없다.
한편 Kebin의 능력은 상속이로 정해지는 것이 아니다. Kebin은 수영을 할 수도 있고, 개발 능력을 가지고 있을 수도 있는데 추상클래스는 다중상속이 불가하기 때문에 인터페이스로 설계해야 하는 것이다.
자바에서 상속은 단 하나의 클래스만 가능하다.
따라서 추상클래스를 상속 받으면 상속받은 추상클래스의 하위타입이 되는 것이다.
반면 하나의 구현체는 여러 인터페이스를 구현할 수 있으며, 어떤 인터페이스 타입으로 호환될 수 있다.
그럼 추상클래스는 무조건 나쁜 거고 인터페이스는 무조건 좋은 걸까?
인터페이스는 추상메서드를 가지고 있고, 이는 구현체에서 반드시 구현해야 한다.
근데 인터페이스를 사용하되, 디폴트 메서드로 공통되는 메서드를 정의해두면, 인터페이스를 가져다 쓰는 개발자의 수고를 덜어낼 수 있지 않나?
라는 생각을 할 수 있다. 하지만 디폴트 메서드의 한계가 있다.
디폴트 메소드 단점
1. Object 메소드인 equals와 hashcode를 디폴트 메소드로 제공 안함. (뒤에서 자세히 설명)
2. 인터페이스는 인스턴스 필드를 가질 수 없고 public이 아닌 정적 메소드를 가질 수 없음.
3. 본인이 만든 인터페이스가 아니면 디폴트 메소드를 추가할 수 없음.
이런 단점을 보면 추상클래스가 살짝 그리워진다.^^
그럼 이러한 단점을 어떻게 보완할 수 있을까?
템플릿 메서드 패턴을 쓰면, 알고리즘 구조를 서브 클래스가 확장할 수 있도록 템플릿으로 제공할 수 있다.
템플릿 메서드 패턴은 전체적으로는 동일하면서 부분적으로는 다른 구문으로 구성된 메서드의 코드 중복을 최소화할 때 유용하다.
TODO 코드 설명
추상클래스는 당중 상속이 불가능하다. 근데 시뮬레이트 다중 상속을 이용하면 추상클래스 여러 개를 다중상속한 것 처럼 만들 수 있다.
TODO 코드 설명
이 말이 이 아이템 중간 중간 등장하는데, 왜 인터페이스 디폴트 메서드는 equals, hashCode, toString 같은 Object 메서드를 재정의할 수 없을까?
재정의하려고 하면 컴파일에러가 뜬다.
MyInterface를 구현하는 클래스의 모습을 상상해보자.
public class MyClass implements MyInterface { ... }
public class MyClass extends Object implements MyInterface {
}
그래서 사실 extends Object 라는 코드가 숨어있는 것이다. 그럼 MyClass 객체는 Object의 equals를 사용해야 하는 걸까 아니면 MyInterface의 equals를 사용해야 하는 걸까...? 절대 알 수 없다.
인터페이스의 디폴트 메서드가 생긴 이유에 대해 생각해보자. 디폴트 메서드는 이미 사용되고 있는 인터페이스를 변경하려고 할 때 구현체 코드도 고쳐야 하는 불편함을 해소하기 위해 생겨났다. 근데 Object 메서드를 디폴트 메서드로 재정의하면, 디폴트 메서드 탄생 이념(?)에 어긋나게 되므로 자바에서 허락하지 않는 것이다.