两个具有相同擦除的方法不一定是覆盖等价的(或者它们之间的签名不是子签名)?

时间:2015-07-04 21:14:22

标签: java generics signature erasure

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

  

在类中给出以下三个泛型方法声明:

     

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

     

static <T> void merge (MyStack<T> s1, MyStack<? extends T> s2) { /*...*/ }

     

static <T> void merge (MyStack<T> s1, MyStack<? super T> 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具有相同的参数类型:

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

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

  •   
  • <A1,...,An>为M的形式类型参数,让<B1,...,Bn>为N的形式类型参数。将N的类型中每个出现的Bi重命名为Ai后,对应的边界类型变量和M和N的参数类型是相同的。

  •   
     

方法m1的签名是方法m2的签名的子签名(如果有的话)   m2与m1具有相同的签名,或者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节),并且在将形式参数类型N调整为M的类型参数,相同的形式参数类型。

     

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

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

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

  •   
     

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

编辑1

简单来说,我的疑问是,从擦除方面的子签名定义中我明白了这一点 “没有擦除的一个签名等于来自其他签名的擦除”......而不是 “擦除后的两个签名都是平等的”......它的微妙但重要 (顺便说一下,覆盖等效定义是基于子签名定义,这就是我用子签名来问的原因)

1 个答案:

答案 0 :(得分:1)

TL; DR

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

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

序言

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

  

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

结合使用
  

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

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

  

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

(用粗体斜体添加单词)。

而您将其解释为

  

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

你的解释是正确的。我不认为这句话含糊不清,因此我认为首先解释它(即 擦除 两个签名是相同的)是不正确的。你可能希望look at this related answer在这里增加我的意见(我找到了它,因为我也想检查我的理解)。

答案(但是......)

您引用的本书部分实际上是在尝试描述重载。

现在 - 在考虑重载时 - JLS (8.4.9) says

  

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

至少从Java 6开始就是这样。这就是override-equivalent和重载之间的联系源于此的地方。

好的 - 所以你的方法会重载,因为它们不是严格的覆盖等价物。正确?

错误。

因为正好在该部分in 8.4.8.3, the JLS之上,所以会产生特定的编译时错误:

  

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

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

  •   
  • m2可从T。

  • 访问   
  • m1的签名不是m2签名的子签名(§8.4.2)。

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

  •   

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

  

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

旁注

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