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

时间:2017-10-29 08:33:07

标签: 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;}   
}

为什么在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也为这个主题添加了想法。

1 个答案:

答案 0 :(得分:3)

尽管原始声明是通用的,但您的f1f3覆盖并非通用。编译允许覆盖的返回类型与原始返回类型不同,因为它们具有相同类型的擦除(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>