【问题标题】:Two methods with same erasure aren't necessary override-equivalent (or they signatures aren't subsignatures between them)?具有相同擦除的两个方法不需要重写等效(或者它们的签名不是它们之间的子签名)?
【发布时间】:2015-07-04 21:14:22
【问题描述】:

我正在阅读关于 jdk6 的令人难以置信的书“java scjp 认证程序员指南”,其中有一个关于泛型覆盖的部分。上面描述了子签名和覆盖等效项,并描述了我引用的一些覆盖等效项的示例:

给定一个类中的以下三个泛型方法声明:

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

简单地说,我怀疑从关于擦除的子签名定义中我明白 “没有擦除的一个签名等于另一个签名的擦除”..而不是 “擦除后的两个签名是相等的”.. 它微妙但重要 (顺便说一下,覆盖等效定义是基于子签名定义的,这就是为什么我在子签名方面提出问题)

【问题讨论】:

  • 但是merge 只有<T> 作为正式类型参数,对吧?据我所知,merge 的所有版本都具有相同的擦除签名。
  • 我同意 aioobe。不确定我是否误解了这一点,但是当您引用“m1 的签名与擦除 m2 的签名”时,您是说这些方法都不是另一个方法的子签名.据我所知,它们都有相同的擦除(检查字节码),所以我不确定你误解了什么。我什至在您的第一个报价中都没有看到任何子签名。如果我错了,请纠正我
  • @vince-emigh (和 aioobe)..这是我的怀疑伙计们,从关于擦除的子签名定义我明白“一个签名 没有 擦除等于从另一个签名中擦除”.. 而不是“擦除后的两个签名相等”.. 它微妙但重要。
  • @VinceEmigh (在我的第一个引用中,我没有提到子签名,但覆盖等效定义是基于子签名的......看看其他 qoutes)
  • 这是一个奇怪的例子——因为 Java 不允许覆盖静态方法,所以我想知道为什么这本书会讨论静态方法的覆盖等效性。我在网上找到了这本书——我只是想看看你正在看第 14.12 节。如果是这样,我认为这本书措辞不佳和/或混乱。在它给出的示例中,它并不是真正谈论 overriding - 而是为什么这些方法不能存在于同一个类中并且被 overloaded 。如果您确认这是您正在寻找的地方,我会尝试将其放入明确的答案中。

标签: java generics signature erasure


【解决方案1】:

TL;DR

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

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


序言

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

"...或者m1的签名和m2的签名擦除一样"

结合

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


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

"或者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,仍然可以看到他们试图展示的效果。

【讨论】:

    猜你喜欢
    • 2013-05-01
    • 1970-01-01
    • 1970-01-01
    • 2019-10-11
    • 2018-12-26
    • 2019-04-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多