今天我偶然发现了一些我甚至不希望编译的Java代码。减少到最低限度,它看起来像这样:
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;
}
}
乍一看,<T>
和foo
中的A
方法的类型参数B
看起来不必要,因为T
未在其他地方使用。无论如何,我发现这在允许冲突的返回值类型在同一个实现中共存起着至关重要的作用:如果省略了<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;
}
}
我不需要修复上面的代码片段,因为这些只是我用来解释我的观点的例子。我只是很想知道编译器为什么表现不同。有人可以解释一下这些规则究竟有什么不同吗?
答案 0 :(得分:21)
虽然第一个示例编译,但它会给出一个未经检查的转换警告:
// 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;
}
这里发生的事情是,通过声明类型参数,A.foo()
和B.foo()
为generic methods。然后,覆盖C.foo()
省略该类型参数。这类似于使用raw type,基本上“选择退出”该方法签名的泛型类型检查。这导致编译器使用继承的方法'erasures:List<String> foo()
和List<Integer> foo()
都变为List foo()
,因此可以C.foo()
实现。
您可以看到,通过在C.foo()
声明中保留type参数,将会出现预期的编译器错误:
// The return type is incompatible with A.foo()
public <T> List<?> foo()
{
return null;
}
同样,如果任一接口方法没有声明类型参数,那么从覆盖中省略类型参数将无法“退出”该方法的泛型类型检查,并返回类型List<?>
仍然不相容。
JLS §8.4.2中包含此行为:
子签名的概念旨在表达两种方法之间的关系,这两种方法的签名不相同,但其中一种方法可以覆盖另一种方法。具体来说,它允许其签名不使用泛型类型的方法覆盖该方法的任何泛化版本。这很重要,因此库设计人员可以独立于定义库的子类或子接口的客户端自由地生成方法。
Angelika Langer的泛型常见问题解答在她的Can a non-generic method override a generic one?部分中对此行为进行了扩展:
现在,让我们探讨非通用子类型方法的示例 覆盖泛型超类型方法。非通用子类型方法是 考虑重写通用超类型方法的版本 签名的删除是完全相同的。
示例(覆盖泛型超类型的非泛型子类型方法 方法):
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() {
此处子类型方法具有签名,即
set(Object)
和get()
,这与超类型方法的删除相同。这些 类型擦除的签名被认为是覆盖等价的。
get
方法有一个缺陷:我们收到一个 未经检查的警告,因为返回类型并不真正兼容。 子类型方法get
的返回类型是Object
,返回类型 超类型方法get是一个无界类型参数。该 子类型方法的返回类型既不与超类型相同 方法的返回类型也不是它的子类型;在这两种情况下 编译器很乐意接受返回类型兼容。 相反,子类型方法的返回类型Object
可以转换为 supertype方法的返回类型通过未经检查的转换。 未经检查的警告表示需要进行类型检查 编译器和虚拟机都无法执行。其他 单词,未经检查的操作不是类型安全的。如果是的话 可转换的返回类型,有人必须确保 子类型方法的返回值与超类型类型兼容 方法的返回类型,但程序员除了可以确保没有人 此