当使用非泛型方法覆盖泛型方法时,为什么subsignature和unchecked规则在返回类型上以这种方式工作?

Cod*_*ete 6 java generics inheritance

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;}   
}
Run Code Online (Sandbox Code Playgroud)

为什么在Derived类中定义重写方法f1()和f3()不会产生编译错误,比如Derived类中的重写f2()方法的定义(它给出编译错误"返回类型与Base.f2()不兼容")?

JLS中的子签名覆盖规则允许覆盖方法(在Derived类中)是非泛型的,而重写方法(在Base类中)是通用的.

未经检查的覆盖规则允许在子类List<String>而不是List<T>在Base类中生成返回类型.

但我无法解释下面的行为差异,我不明白为什么fin()和f3()覆盖Derived类中的定义成功编译(在Eclipse,SE8上),忽略了f3()的有界类型参数所施加的限制f1()的有界通配符!

PS我的猜测 - 在衍生编译器中的f1()和f3()中将两个方法视为仅返回"原始"列表 - 编译器首先进行擦除(此时仅在Derived!中),然后在Derived中比较这些已擦除的方法Base中未删除(迄今为止)的方法.现在未经检查的覆盖规则是正常的(并且不需要检查边界 - 这根本不可能),编译器决定这是正确的覆盖和编译更进一步......在Base.f1()和Base的编译泛型结束时的某处.f3()也擦掉:)))

这个SO答案也为这个主题增加了想法.

Jon*_*eet 3

尽管原始声明是通用的,但您的f1和覆盖不是通用的。f3编译允许覆盖的返回类型与原始返回类型不同,因为它们具有相同的类型擦除 ( List)。我认为这是JLS 8.4.5的结果,尽管坦率地说,我发现规范的这一部分有点令人困惑。

如果您将覆盖再次更改为通用:

<T> List<String> f1() {return null;}
<T extends Number> List<String> f3() {return null; }
Run Code Online (Sandbox Code Playgroud)

...然后都无法编译:

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()
Run Code Online (Sandbox Code Playgroud)

请注意,即使在原始代码中,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>
Run Code Online (Sandbox Code Playgroud)