generics方法实现中不同的返回值types

今天,我偶然发现了一些我不希望编译的Java代码。 减less到最低限度,看起来像这样:

import java.util.List; interface A { <T> List<String> foo(); } interface B { <T> List<Integer> foo(); } class C implements A, B { @Override public List<?> foo() { return null; } } 

乍一看, AB foo方法的types参数<T>看起来没有必要,因为在其他地方没有使用T 无论如何,我发现这在允许相互冲突的返回值types在同一个实现中共存的过程中发挥了至关重要的作用:如果一个或两个<T>被遗漏,代码不会被编译。 这里的非工作版本:

 import java.util.List; interface A { List<String> foo(); } interface B { List<Integer> foo(); } class C implements A, B { @Override public List<?> foo() { return null; } } 

我不需要修正上面的代码片断,因为这些只是我解释我的观点的例子。 我只是想知道为什么编译器的行为与他们不同。 有人可以解释什么规则正在使这里的区别?

虽然第一个例子是编译的,但它会给出一个未经检查的转换警告:

 // Type safety: The return type List<?> for foo() from the type C needs // unchecked conversion to conform to List<String> public List<?> foo() { return null; } 

这里发生的是通过声明types参数, A.foo()B.foo()是通用的方法 。 然后,覆盖的C.foo()省略了那个types参数。 这类似于使用原始types ,实质上是对该方法签名的genericstypes检查“select退出”。 这导致编译器使用inheritance的方法的擦除,而不是: List<String> foo()List<Integer> foo()都成为List foo() ,因此可以由C.foo()

你可以看到,通过在C.foo()声明中保留types参数,将会出现预期的编译器错误:

 // The return type is incompatible with A.foo() public <T> List<?> foo() { return null; } 

同样,如果任一接口方法没有声明一个types参数,那么从重写中省略一个types参数将无法“退出”该方法的genericstypes检查,并且返回typesList<?>保持不兼容。

JLS第8.4.2节介绍了这一行为:

子签名的概念旨在表示两个方法之间的关系,其签名不相同,但是可以重写另一个方法。 具体来说,它允许一个方法,其签名不使用genericstypes来覆盖该方法的任何generified版本。 这一点非常重要,这样图书馆的devise者就可以独立于定义图书馆的子类或子接口的客户机来自由生成方法。

Angelika Langer的generics常见问题在她的章节中对这种行为进行了扩展一个非generics方法是否可以覆盖generics方法? :

现在,让我们探讨一个非generics子types方法覆盖generics超types方法的例子。 如果签名的擦除是相同的,则非generics子types方法被认为是generics超types方法的重写版本。

示例(覆盖一般超types方法的非generics子types方法):

 class Super { public <T> void set( T arg) { ... } public <T> T get() { ... } } class Sub extends Super { public void set( Object arg) { ... } // overrides public Object get() { ... } // overrides with unchecked warning } 

 warning: get() in Sub overrides <T>get() in Super; return type requires unchecked conversion found : Object required: T public Object get() { 

这里的子types方法具有签名,即set(Object)get() ,它们与超types方法的删除相同。 这些types擦除签名被认为是覆盖等效的。

get方法有一个缺陷:我们收到一个未经检查的警告,因为返回types不是真的兼容。 子types方法get的返回types是Object ,超types方法get的返回types是一个无界的types参数。 子types方法的返回types既不是超types方法的返回types,也不是它的子types; 在这两种情况下,编译器会高兴地接受返回types为兼容的。 相反,子types方法的返回typesObject可以通过未经检查的转换转换为超types方法的返回types。 未经检查的警告表示需要types检查,编译器和虚拟机都不能执行。 换句话说,未经检查的操作不是types安全的。 在可转换返回types的情况下,人们必须确保子types方法的返回值与超types方法的返回types是types兼容的,但是程序员除了可以保证这一点外,没有人能够确保这一点。