用Java转换的懒类?
问题内容:
有人可以启发我为什么我没有得到ClassCastException
这个片段吗?我对为什么它不能按我预期的那样非常感兴趣。我现在不在乎这是否是不好的设计。
public class Test {
static class Parent {
@Override
public String toString() { return "parent"; }
}
static class ChildA extends Parent {
@Override
public String toString() { return "child A"; }
}
static class ChildB extends Parent {
@Override
public String toString() { return "child B"; }
}
public <C extends Parent> C get() {
return (C) new ChildA();
}
public static void main(String[] args) {
Test test = new Test();
// should throw ClassCastException...
System.out.println(test.<ChildB>get());
// throws ClassCastException...
System.out.println(test.<ChildB>get().toString());
}
}
这是Java版本,编译和运行输出:
$ java -version
java version "1.7.0_17"
Java(TM) SE Runtime Environment (build 1.7.0_17-b02)
Java HotSpot(TM) 64-Bit Server VM (build 23.7-b01, mixed mode)
$ javac -Xlint:unchecked Test.java
Test.java:24: warning: [unchecked] unchecked cast
return (C) new ChildA();
^
required: C
found: ChildA
where C is a type-variable:
C extends Parent declared in method <C>get()
1 warning
$ java Test
child A
Exception in thread "main" java.lang.ClassCastException: Test$ChildA cannot be cast to Test$ChildB
at Test.main(Test.java:30)
问题答案:
类型擦除:泛型仅是一种语法功能,编译器将其删除(出于兼容性原因),并
在需要时 用强制转换代替。
在运行时,该方法C get
不知道类型C
(这就是为什么无法实例化的原因new C()
)。的调用test.<ChildB>get()
实际上是的调用test.get
。return (C) new ChildA()
之所以被转换为,return (Object) new ChildA()
是因为无边界类型的擦除C
是Parent
(其最左边界)。然后,由于println
需要使用Object
as参数,因此不需要强制转换。
另一方面,test.<ChildB>get().toString()
失败test.<ChildB>get()
是因为ChildB
调用之前将其强制转换为toString()
。
注意,类似的调用myPrint(test.<ChildB>get())
也会失败。调用时Parent
,get
将ChildB
完成从return
by 到type的转换myPrint
。
public static void myPrint(ChildB child) {
System.out.println(child);
}