覆盖子类作为参数和泛型:它在Java Lang Spec中的位置?

4 java generics

我遇到类似以下的Java代码:

public interface BaseArg {
}

public class DerivedArg implements BaseArg {
}

public abstract class Base <A extends BaseArg> {

 A arg;

 void doIt() {
  printArg(arg);
 }

 void printArg(A a) {
  System.out.println("Base: " + a);
 }

}


public class Derived extends Base<DerivedArg> {

 void printArg(DerivedArg a) {
  System.out.println("Derived: " + a);
 }


 public static void main(String[] args) {
  Derived d = new Derived();
  d.arg = new DerivedArg();
  d.doIt();
 }

}
Run Code Online (Sandbox Code Playgroud)

(随意将其拆分为文件并运行它).

此代码最终调用Derived printArg.我意识到这是唯一符合逻辑的事情.但是,如果我手动在通用Base上执行"擦除",将所有出现的A替换为BaseArg,则覆盖会发生故障.我现在得到Base的printIt版本.

似乎"擦除"不完全 - 不知何故printArg(A a)与printArg(BaseArg a)不同.我在语言规范中找不到任何基础...

我在语言规范中缺少什么?这不是很重要,但它让我烦恼:).

mer*_*ike 6

请注意,导出方法调用.问题是为什么,考虑到它们的擦除签名不是覆盖等价的.

编译类Derived时,编译器实际上发出两个方法:方法printArg(DerivedArg)和合成方法printArg(BaseArg),它根据类型参数无法理解的虚拟机来覆盖超类方法,并委托给printArg (DerivedArg).您可以通过在printArt(DerivedArg)中抛出异常,同时在Base类型的引用上调用异常并检查堆栈跟踪来验证这一点:

Exception in thread "main" java.lang.RuntimeException
        at Derived.printArg(Test.java:28)
        at Derived.printArg(Test.java:1)     << synthetic
        at Base.doIt(Test.java:14)
        at Test.main(Test.java:39)
Run Code Online (Sandbox Code Playgroud)

至于在Java语言规范中找到这个,我首先也错过了它,因为它不是,正如人们所预料的那样,指定了覆盖或子签名关系的讨论,而是在"参数化类型的成员和构造函数"中(§4.5) .2),它揭示了在检查覆盖等价之前,超类的形式类型参数在语法上被子类中的实际类型参数替换.

也就是说,与普遍的假设相反,覆盖等价不受擦除的影响.