[英]Why does this use of Generics not throw a runtime or compile time exception?
我在类中有一个方法,它具有通过使用泛型指定的返回类型。
public class SomeMain {
public static void main(String[] args) {
Foo<Integer> foo = new Foo<Integer>();
System.out.println(foo.getFoo()); // Works, prints out "Foo"
}
public static class Foo<E> {
public E getFoo() {
return (E) "Foo";
}
}
}
使用泛型返回类型,我假设上面示例中的返回值将评估为:
return (Integer) "Foo"; // Inconvertible types !!!
而是返回并正确打印String
。
如果我将调用更改为:我收到编译错误:
String fooString = foo.getFoo(); // Compile error, incompatible types found
System.out.println(fooString);
我错过了什么来帮助我理解这里发生的事情以及为什么原始版本不会导致编译错误。
这是因为重载决议解决了对println(Object)
println
调用,因为没有println(Integer)
。
请记住,Java的泛型在运行时会被删除。 像(E) "Foo"
这样的演员表被删除,并被移动到呼叫站点。 有时这不是必需的,因此只有在需要时才会将事物转换为正确的类型。
换句话说,在getFoo
没有执行强制转换。 语言规范支持:
第5.5.2节Checked Casts和Unchecked Casts
演员阵容完全不受限制。
没有对这样的演员表执行运行时动作。
擦除后, getFoo
返回Object
。 然后它被传递到println(Object)
,这非常好。
如果我调用此方法并传递foo.getFoo
,我将收到一个错误:
static void f(Integer i) {
System.out.println(i);
}
// ...
f(foo.getFoo()); // ClassCastException
因为这次需要进行铸造。
System.out.println
没有带Integer
的重载。 所以这句话:
System.out.println(foo.getFoo());
正在调用System.out.println(Object);
。
要验证它是否会失败,请尝试:
Foo<Integer> foo = new Foo<Integer>();
Integer fooInt = foo.getFoo(); //class cast exception
以下将以相同的方式失败:
public static void main(String[] args) throws Exception {
Foo<Integer> foo = new Foo<Integer>();
print(foo.getFoo()); //Would also fail with a class cast exception
}
static void print(Integer in) {
System.out.println(in);
}
由于显而易见的原因,这是编译失败:
String fooString = foo.getFoo(); //can't work
foo
是Foo<Integer>
, foo.getFoo()
返回一个Integer
,编译器可以选择它。
我想补充一点,在类型擦除过程中,Java编译器用Object
替换unbounded类型参数E
,因此Foo
类实际编译为:
public static class Foo {
public Object getFoo() {
return "Foo";
}
}
这就是为什么以下代码有效(不需要强制转换):
Object obj = foo.getFoo();
System.out.println(obj);
同时,下一个代码片段会产生编译时错误,如预期的那样:
Foo<Integer> foo = new Foo<Integer>();
String fooString = foo.getFoo(); // you're trying to trick the compiler (unsuccessfully)
^
incompatible types: Integer can not be converted to String
这是泛型的主要责任 - 编译时检查。
然而,故事的另一面 - 执行时间演员。 例如,如果你写:
Integer value = foo.getFoo();
你得到一个在运行时抛出的ClassCastException
(Java编译器插入一个checkcast
指令,检查foo.getFoo()
的结果是否可以foo.getFoo()
为Integer
)。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.