具有相同擦除的两种方法不是必须覆盖等效的(或者它们之间的签名不是子签名)?

我正在为jdk6阅读令人难以置信的书“java scjp认证的程序员指南”,以及关于generics覆盖的部分。 在它上面描述了subsignature和override-equivalent,并描述了我引用的覆盖等价的一些例子:

给定以下三个类中的generics方法声明:

static void merge (MyStack s1, MyStack s2) { /*...*/ }

static void merge (MyStack s1, MyStack s2) { /*...*/ }

static void merge (MyStack s1, MyStack s2) { /*...*/ }

擦除后,所有三种方法的签名是: merge(MyStack, MyStack)即方法的签名是覆盖等价的,因此这些方法不会重载。

我并不完全同意这些方法是覆盖等价的,事实上我认为这些方法有一个“擦除名称冲突”,但没有一个是另一个的子签名……可能是错误的,所以我想对此有所了解。

子签名的定义让我觉得它们不是它们之间的子签名。

在JSL 6#8.4.2方法签名中( http://docs.oracle.com/javase/specs/jls/se6/html/classes.html#8.4.2 )

如果两个方法具有相同的名称和参数类型,则它们具有相同的签名。 如果满足以下所有条件,则两个方法或构造函数声明M和N具有相同的参数类型:

  • 他们。 具有相同数量的forms参数(可能为零)

  • 它们具有相同数量的类型参数(可能为零)

  • 为M的forms类型参数,并让为N的forms类型参数。将N的类型中每个出现的Bi重命名为Ai的边界对应的类型变量和M和N的参数类型是相同的。

如果m2与m1具有相同的签名,或者m1的签名与m2的签名擦除相同,则方法m1的签名是方法m2的签名的子签名。

...

如果m1是m2的子签名或m2是m1的子签名,则两个方法签名m1和m2是覆盖等价的。

在JSL 8#8.4.2中。 方法签名( http://docs.oracle.com/javase/specs/jls/se8/html/jls-8.html#jls-8.4.2 )

两个方法或构造函数M和N具有相同的签名,如果它们具有相同的名称,相同的类型参数(如果有的话)(第8.4.4节),并且在将forms参数类型N调整为类型参数之后M,相同的forms参数类型。

方法m1的签名是方法m2的签名的子签名,如果:

  • m2与m1具有相同的签名,或

  • m1的签名与m2签名的擦除相同。

如果m1是m2的子签名或m2是m1的子签名,则两个方法签名m1和m2是覆盖等价的。

编辑1

简单来说,我的疑问是,根据关于擦除的子签名定义,我理解“没有擦除的一个签名等于来自其他签名的擦除”……而不是“擦除后的两个签名相等”。它的微妙但重要(顺便说一下,覆盖等价定义是基于子签名定义,这就是为什么我用子签名来问)

TL; DR

在我看来,这本书的措辞并没有很好地结合在一起。 根据JLS(8.4.9) (重新定义:如果存在两个具有相同名称的方法但不是覆盖等效的方法,那么它们将重载),根据覆盖等效的否定来定义重载。

但是给出的示例是方法不是覆盖等价的,但是由于其他原因(由于JLS 8.4.8.3中指定的特定编译时错误而导致DO导致编译时错误),因此不会重载。


前言

据我了解,你提出了一个关于这句话子句的确切语义的问题:

“……或m1的签名与m2的签名擦除相同”

与…结合使用

如果m1是m2的子签名或m2是m1的子签名,则m1和m2是覆盖等效的。


你的书暗示这应该被解释为

“或m1签名的擦除与m2签名的擦除相同”

(用粗斜体添加单词)。

而你会把它解释为

“或m1的签名( 没有擦除 )与m2的签名擦除相同”

你的解释是正确的。 我不认为这句话是模棱两可的,因此我认为以第一种方式解释它(即两个签名的擦除是相同的)是不正确的。 你可能想看看这个相关的答案 ,在这里增加我的观点的重量(我发现它,因为我也想检查我的理解)。


答案(但……)

你引用的那本书的部分实际上是试图描述重载。

现在 – 当考虑重载时 – JLS(8.4.9)说 :

如果一个类的两个方法(无论是在同一个类中声明,还是由一个类inheritance,或者一个是声明的,一个是inheritance的)都具有相同的名称,但签名不是覆盖等效的,那么方法名称就是超载。

这至少与Java 6一致。这是override-equivalent和重载之间的联系源于此。

好的 – 所以你的方法会超载,因为它们不是严格的覆盖等价物。 对?

错误。

因为正好在8.4.8.3中的那一节上面, JLS会输入一个特定的编译时错误:

如果类型声明T具有成员方法m1并且存在以T声明的方法m2或T的超类型使得以下所有条件都为真,那么这是编译时错误:

  • m1和m2具有相同的名称。

  • m2可从T访问。

  • m1的签名不是m2签名的子签名(第8.4.2节)。

  • m1的签名或某些方法m1覆盖(直接或间接)具有与m2的签名相同的擦除或某种方法m2覆盖(直接或间接)。

这是您示例中的方案。 在该部分的下方,它阐明了为什么有必要:

这些限制是必要的,因为generics是通过擦除来实现的。 上面的规则意味着在同一个类中声明的具有相同名称的方法必须具有不同的擦除。 它还意味着类型声明不能实现或扩展同一通用接口的两个不同的调用。

边注

书中的例子很奇怪,因为Java不允许覆盖静态方法(而是子类中方法的签名可以隐藏超类中的方法)。 在我看来,这使得对于学习者来说,不被覆盖等同的概念有点棘手。 但是,您可以删除static ,但仍然可以看到他们试图演示的效果。