아래 코드를 보시고 1, 2번 라인중에 어디서 에러가 날 지를 찾아보세요.
interface B {
void doB();
}
class D implements B {
public void doB() {}
}
interface H {
B getB();
}
class HImpl implements H {
public B getB() {
return new D(); // 1)
}
}
...
H h = new HImpl();
D d = h.getB(); // 2)
네, 2번 라인입니다.
그럼 다음 코드는?
interface B1 {
void doB1();
}
interface B2 {
void doB2();
}
class D implements B1, B2 {
public void doB1() {}
public void doB2() {}
}
interface H {
<T extends B1 & B2> T getB();
}
class HImpl implements H {
public <T extends B1 & B2> T getB() {
return new D(); // 1)
}
}
...
H h = new HImpl();
D d = h.getB(); // 2)
네, 1번입니다. 잘 이해가 되질 않아 사내 메일링 리스트에 물어보니
I think you're seeing and interpreting it as "this method can return anything that implements both interfaces". What it's actually saying is "the caller is going to tell you a specific class that implements both interfaces, and you must return one of those".
이랍니다. 즉 T 타입이 아직 결정되지 않은 상태라 D와 T는 compatible한 타입이 아닌거죠.
강제로 casting을 해서 컴파일이 되게 만들면 그 코드는 runtime error (ClassCastException)를 발생하게 됩니다. 다음 코드를 보세요. 즉 이런 방법으론 도저히 type-safe한 코드를 작성할 수 없습니다.
class D2 implements B1, B2 {
public void doB1() {}
public void doB2() {}
}
...
D2 d = h.getB(); // ClassCastException!!
이 같은 상황을 방지하기 위해 1번 라인에서 에러를 내 주는 것이죠.
코딩 가이드라인을 만들어 보자면 "method의 parameter로 사용되지 않는 type variable을 사용하면 안된다"입니다. 다음과 같은 경우는 parameter로도 사용되고 있으므로 문제가 없는 경우들입니다.
<T> T makeInstance(Class<T> cls) { return cls.newInstance(); }
...
<T extends Comparable<? super T>> T min(T a, T b) {
return a.compareTo(b) < 1 ? a : b;
}
위의 예제 코드같은 경우 아래와 같은 방법으로 type-safe하게 작성될 수 있습니다.
interface B1 {
void doB1();
}
interface B2 {
void doB2();
}
class D implements B1, B2 {
public void doB1() {}
public void doB2() {}
}
interface H <T extends B1, B2> {
T getB();
}
class HImpl implements H<D> {
public D getB() {
return new D(); // 1)
}
}
...
H<D> h = new HImpl();
D d = h.getB(); // 2)
사실... 아직도 잘 모르겠어요. Java generic. =(