[英]Different return value types in implementation of generic methods
今天我偶然发现了一些我甚至无法编译的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;
}
}
乍一看, A
和B
中的foo
方法的类型参数<T>
看起来是不必要的,因为在其他任何地方都没有使用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;
}
}
我不需要修复上面的代码片段,因为这些只是我用来解释我的观点的例子。 我只是很想知道编译器为什么表现不同。 有人可以解释一下这些规则究竟有什么不同吗?
虽然第一个示例编译,但它会给出一个未经检查的转换警告:
// 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()
是通用方法 。 然后,重写的C.foo()
省略了该类型参数。 这与使用原始类型类似,基本上“选择退出”该方法签名的泛型类型检查。 这导致编译器使用继承方法的擦除 : 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的泛型常见问题解答在她的部分中扩展了这种行为。 非泛型方法是否可以覆盖泛型方法? :
现在,让我们探讨一个非泛型子类型方法覆盖泛型超类型方法的示例。 如果签名的擦除相同,则非泛型子类型方法被认为是通用超类型方法的覆盖版本。
示例(覆盖通用超类型方法的非泛型子类型方法):
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
可以通过未经检查的转换转换为超类型方法的返回类型。 未经检查的警告表示无需编译器和虚拟机执行类型检查。 换句话说,未经检查的操作不是类型安全的。 在可转换返回类型的情况下,有人必须确保子类型方法的返回值与超类型方法的返回类型类型兼容,但除了程序员之外没有人可以确保这一点。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.