我在类中有一个方法,该方法具有使用泛型指定的返回类型。
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);
我缺少什么来帮助我了解这里发生了什么以及为什么原始版本没有导致编译错误。
答案 0 :(得分:18)
这是因为过载解析将您的println
调用解析为println(Object)
,因为没有println(Integer)
。
请记住,Java的泛型会在运行时删除。像(E) "Foo"
这样的演员表也将被删除,并移至呼叫站点。有时这不是必需的,因此仅在需要时才将内容强制转换为正确的类型。
换句话说,在getFoo
内部不执行任何强制转换。语言规范支持这一点:
第5.5.2节已检查的演员表和未检查的演员表
演员表是完全未经检查的演员表。
不对此类转换执行运行时操作。
删除后,getFoo
返回Object
。并将其传递给println(Object)
,这很好。
如果我调用此方法并传递foo.getFoo
,则会会出现错误:
static void f(Integer i) {
System.out.println(i);
}
// ...
f(foo.getFoo()); // ClassCastException
因为这一次需要被投射。
答案 1 :(得分:3)
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
,编译器可以选择它。
答案 2 :(得分:2)
我想补充一点,在type erasure过程中,Java编译器将无界类型参数E
替换为Object
,因此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()
的结果是否可以转换为Integer
)。 / p>