public class Base {
<T> List<? extends Number> f1() {return null;}
List<? extends Number> f2() {return null;}
<T extends Number> List<T> f3() {return null; }
}
class Derived extends Base {
List<String> f1() {return null;} // compiles fine !!!
List<String> f3() {return null; } // compiles fine !!!
// compile ERR: return type is incompatible with Base.f2()
List<String> f2() {return null;}
}
为什么在Derived类中定义重写方法f1()和f3()不会产生编译错误,例如在Derived类中重写f2()方法的定义(这会导致编译错误&#34;返回类型不兼容使用Base.f2()&#34; )?
JLS中的子签名覆盖规则允许覆盖方法(在Derived类中)是非泛型的,而重写方法(在Base类中)是通用的。
未经检查的覆盖规则允许在Base类中的子类List<String>
而不是List<T>
中生成返回类型。
但我无法解释下面的行为差异,我不明白为什么fin()和f3()覆盖Derived类中的定义成功编译(在Eclipse,SE8上),忽略有界类型参数强加的限制对于f3()和有界通配符f1()!
P.S。我的猜测 - 在Derived编译器中的f1()和f3()将两个方法视为仅返回&#34; raw&#34; List - 编译器首先进行擦除(此时仅在Derived!中),然后将Derived中的这些擦除方法与Base中未擦除(迄今为止)的方法进行比较。现在未经检查的覆盖规则是正常的(并且不需要检查边界 - 这根本不可能),编译器决定这是正确的覆盖和编译更进一步...在Base.f1()和Base的编译泛型结束时的某处.f3()也删除了:)))
This SO answer也为这个主题添加了想法。
答案 0 :(得分:3)
尽管原始声明是通用的,但您的f1
和f3
覆盖并非通用。编译允许覆盖的返回类型与原始返回类型不同,因为它们具有相同类型的擦除(List
)。我认为来自JLS 8.4.5,但坦率地说,我发现规范的这一部分有点令人困惑。
如果您将覆盖更改为通用:
<T> List<String> f1() {return null;}
<T extends Number> List<String> f3() {return null; }
...然后都无法编译:
error: <T#1>f1() in Derived cannot override <T#2>f1() in Base
<T> List<String> f1() {return null;}
^
return type List<String> is not compatible with List<? extends Number>
where T#1,T#2 are type-variables:
T#1 extends Object declared in method <T#1>f1()
T#2 extends Object declared in method <T#2>f1()
error: <T#1>f3() in Derived cannot override <T#2>f3() in Base
<T extends Number> List<String> f3() {return null; }
^
return type List<String> is not compatible with List<T#1>
where T#1,T#2 are type-variables:
T#1 extends Number declared in method <T#1>f3()
T#2 extends Number declared in method <T#2>f3()
请注意,即使在原始代码中,javac
也会针对不安全的转化发出警告,如果您使用-Xlint:unchecked
,则会提供详细信息:
return type requires unchecked conversion from List<String> to List<? extends Number>
return type requires unchecked conversion from List<String> to List<T>