8

我正在阅读关于 jdk6 的令人难以置信的书“java scjp 认证程序员指南”,其中有一个关于泛型覆盖的部分。上面描述了 subsignature 和 override-equivalent 并描述了一些我引用的 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 的参数类型相同。

如果 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 节),并且在将 N 的形式参数类型调整为类型参数之后,则它们具有相同的签名的 M,形参类型相同。

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

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

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

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

编辑 1

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

4

1 回答 1

1

TL;博士

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

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


前言

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

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

结合

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


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

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

(加粗斜体字)。

而您会将其解释为

“或者m1的签名(没有擦除)和m2的签名的擦除一样”

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


回答(但是……)

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

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

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

至少从 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 覆盖(直接或间接)的签名具有相同的擦除。

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

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

边注

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

于 2015-07-06T11:12:03.230 回答