用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.getreturn (C) new ChildA()之所以被转换为,return (Object) new ChildA()是因为无边界类型的擦除CParent(其最左边界)。然后,由于println需要使用Objectas参数,因此不需要强制转换。

另一方面,test.<ChildB>get().toString()失败test.<ChildB>get()是因为ChildB调用之前将其强制转换为toString()

注意,类似的调用myPrint(test.<ChildB>get())也会失败。调用时ParentgetChildB完成从return
by 到type的转换myPrint

public static void myPrint(ChildB child) {
  System.out.println(child);
}