자바에는 여러 구현을 허용하는 자료형을 만드는 방법이 두가지 있습니다.
추상형 클래스와 인터페이스입니다.
이 두가지의 차이점은 추상클래스는 구현된 메소드를 포함할 수 있지만, 인터페이스는 아니라는 점입니다... 는 점이었지만
자바 1.8부터 default 를 활용하여 언터페이스도 body를 가질 수 있게 되었습니다!
public interface Test {
public void existingMethod();
default public void newDefaultMethod() {
System.out.println("New default method is added in interface");
}
}
덕분에 우리는 공부할 게 늘었습니다.
추상형 클래스는 구현하려면 상속(계승)을 써야합니다.
자바에서는 멀티계승을 허용하지 않기 때문에 추상형 클래스로 유연한 자료형을 사용하려 한다면 많은 제한이 따르게 됩니다.
하지만 인터페이스는 클래스 계층(hierarchy)에 상관없이 복수로 구현이 가능합니다.
또한 기존의 클래스를 인터페이스 구현에 추가하는 일도 간단합니다.
인터페이스를 만든 후 클래스의 메소드를 확인해서 추가한다음
implements 만 붙여 주어 구현하면 됩니다.
하지만 상속을 전제로 하는 추상형 클래스에는 제한이 붙게됩니다.
다음은 간단하게 만들어 본 추상클래스와 계층 그래프입니다
abstract public class A {
public abstract void aa();
}
abstract public class B extends A {
public abstract void b();
}
public class C extends B{
@Override
public void aa() {}
@Override
public void b() {}
}
B는 추상클래스인 A를 상속하지만, 본인이 추상클래스여서 구현을 강제받지 않습니다.
대신 마지막에 상속하는 C에게 그 역할을 넘기죠.
C는 두가지 모두를 구현해야 합니다.
이처럼 추상클래스는 클래스 계층안에서 밀접한 관계를 맺고있습니다.
때문에 이 중간에 추상클래스를 추가해서 구현을 하려는 것은 불가능하다고 보는 것이 맞습니다.
인터페이스는 믹스인(minxin)을 정의하는데 이상적입니다.
믹스인이 뭘까요? 책에서는
클래스가 "주 자료형"이외에 추가로 구현할 수 있는 자료형으로, 어떤 선택적 기능을 제공한다는 사실을 선언하기 위해서 쓰인다.
라고 설명해서 무슨 말인지 딱 안와닿는데
예제코드도 보고 이것저것 찾아보니 결국 멀티 인터페이스 구현이된다는 이야기더군요.
public class C implements D, F {}
선택적으로 D, E, F 중 필요한 부분을 섞어(mix) 넣어(in)서 믹스인이라고 부르는 듯 합니다.
다시 인터페이스의 장점으로 돌아가보자면
public interface Singer {
AudioClip sing(Song s);
}
public interface Songwriter {
Song compose(boolean hit);
}
가수와 작곡가 클래스가 있습니다.
하지만 가수 중에는 작곡가를 겸하는 분들도 있죠.
public interface SingerSongwriter extends Songwriter, Singer{
AudioClip strum();
void actSensitive();
}
그럴 때는 인터페이스를 extends 한 형태로 만들어 주면 됩니다.
이렇듯 인터페이스는 계층에 상관없이 자료형 프레임워크를 만들 수 있는 아주 유연한 기능을 제공합니다
만약 이런 자료형을 구현하고 싶은데 인터페이스가 없다면?
SingerSongwriter 라는 클래스를 구현하고 그 안에 songwriter 와 singer를 넣어놔야합니다.
3개니까 이렇게 끝나지만, 만약 백개쯤 된다면?
조합을 맞춘 클래스가 수천개는 가볍게 넘을 겁니다. 이런 현상을 조합 폭증(combinatiorial explosion)이라고 합니다.
이러한 현상을 가볍게 해결해주는 인터페이스는 아주 유용한 도구죠.
인터페이스안에서는 메소드 구현을 둘 수 없지만, 그렇다고 프로그래머가 사용할 수 있는 코드를 제공할 방법이 없는 것은 아닙니다.
추상 골격 구현(abstract skeletal implementation) 클래스를 중요 인터페이스 마다 두면, 인터페이스의 ㅈ아점과 추상 클래스의 장점을 살릴 수 있습니다.
책을 이해한 대로 코드를 한번 만들어봤습니다.
public interface A {
void aa();
void bb();
}
A는 인터페이스 입니다.
public abstract class B implements A {
@Override
public void aa(){
System.out.println("aa");
}
}
B는 A를 상속하는 추상 클래스입니다.
추상클래스이기에 구현을 강제하지는 않습니다만
override는 가능합니다. 간단하게 aa를 출력해볼까요.
public class C extends B{
@Override
public void bb() {
this.aa();
}
}
이번엔 추상클래스 B를 상속하는 C를 만들어봅시다.
B 추상클래스는 bb를 오버라이드 하지 않았기 때문에
bb의 오버라이드가 강제됩니다. aa를 호출해봅시다.
public class D {
public static void main(String[] args){
C c = new C();
c.aa();
}
}
그리고 C 객체를 생성해서
aa를 호출해보죠.
정상적으로 aa가 나오게 됩니다.
이 코드에서 말하고 싶은 점은
C의 aa는 바디가 있는 추상클래스에서, bb는 인터페이스에서 가져옵니다.
C가 객체를 인터페이스를 구현하는 효과를 가지지만 구체화 된 바디가 있는 메소드를 그대로 가져올 수 있죠.
관습적으로 이러한 골격 ㄱ구현 클래스의 이름을 AbstractInterface라고 정의합니다.
자바 1.8버전에는 interface에서 default를 사용할 수 있으니
바디가 있는 유연성 있는 구현을 하고싶다면 굳이 이 추상골격구현을 안써도 되겠네요.
방법의 하나로만 알아두기로 합시다.
자바의 API에는 이러한 추상골격구현 방법으로 만들어진 클래스도 있다고 합니다
이펙티브 자바에서 꼽는 인터페이스의 단한가지 단점은 추상클래스가 인터페이스보다 발전시키기 쉽다는 성격입니다.
추상클래스의 구현은 계층(hierarchy)에 들어가기 때문에 위에서 메소드를 새로 추가한다면
밑의 모든 클래스들은 그 메소드를 사용할 수 있습니다.
인터페이스는 인터페이스에 새로운 메소드를 추가하면 밑에서 자료형이 깨지죠. 새로운 메소드를 구현하라고 컴파일 에러를 날리죠.
하지만 이것도 옛말이 됬네요.
저도 default를 써본적 없으니 한번 써보려고 코드를 짜봤습니다.
public interface A {
void aa();
void bb();
}
aa, bb라는 메소드를 가지는 A 인터페이스가 있습니다.
public class B implements A{
@Override
public void aa() {
}
@Override
public void bb() {
}
}
A를 구현하는 B를 만들면, 두개를 오버라이드 하라고 컴파일 에러를 줘서 구현을 해줘야 합니다.
여기서 A에 메소드를 추가해볼까요?
cc를 추가해봤습니다.
B에서 당연히 컴파일 에러가 나지요?
다시 A로 돌아가서
default를 만들고 바디를 붙여줍시다.
에러가 사라집니다.
B에서 구현하지 않더라도
새로운 클래스를 만들어서 A인터페이스에서 정의한 내용 그대로 사용이 가능합니다.
이렇듯 public 인터페이스를 구현하는 기존 클래스를 깨뜨리지 않고
새로운 메소드를 인터페이스에 추가할 방법은 없다는 이야기도 옛말이 되어버렸습니다.
왜 default를 만들었나 잠깐 생각해보았는데, 추상 골격 클래스를 사용해도
결국 단일 상속만 허용하는 자바에서는 추상골격 클래스도 유연성이 떨어지는 반쪽자리 방법이라 판단했나봅니다.
결론을 짓자면 자바에서 abstract 는 어지간하면 사용하지 않는 편이 좋을 것 같군요
'프로그래밍 > Java' 카테고리의 다른 글
이펙티브 자바 규칙 19 - 인터페이스는 자료형을 정의할 때만 사용하라 (0) | 2018.02.18 |
---|---|
이펙티브 자바 규칙 17 - 계승을 위한 문서를 갖추거나, 그럴 수 없다면 금지하자 (0) | 2018.02.15 |
이펙티브 자바 규칙 16 - 계승하는 대신 구성하라 (0) | 2018.02.14 |