当前位置: 首页 > 知识库问答 >
问题:

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

孙佐
2023-03-14

我正在读一本关于jdk6的难以置信的书《java scjp认证程序员指南》,其中有一节是关于泛型重写的。上面描述了子签名和替代等效物,并描述了我引用的替代等效物的一些示例:

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

静态

静态

静态

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

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

子项的定义使我认为它们不是它们之间的子项。

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

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

>

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

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

...

两个方法签名m1和m2是重写等效的,如果m1是m2的子签名,或者m2是m1的子签名。

在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签名的子签名,如果:

>

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

当m1是m2的子签名或m2是m1的子签名时,两个方法签名m1和m2是覆盖等价的。

简单来说,我的疑问是,根据子签名关于擦除的定义,我理解“一个未擦除的签名等于另一个签名的擦除”。。并不是说“擦除后的两个签名是相等的”。。它微妙但重要(顺便说一句,覆盖等价定义是基于子签名定义的,这就是为什么我要用子签名的形式提问)


共有1个答案

司徒正信
2023-03-14

在我看来,这本书的措辞在这里不太一致。根据JLS(8.4.9)(意译:如果存在两个同名的方法,但它们不是重写等价的,那么它们将重载),重载是根据重写等价性的否定来定义的。

但是给出的例子是一个方法不是重写等效的,但是由于其他原因(名称冲突——JLS8.4.8.3中指定的特定编译时间错误)导致编译时间错误,因此不会重载的例子。

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

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

结合

m1和m2是重写等效的,如果m1是m2的子签名,或者m2是m1的子签名。

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

"或删去m1的签署与删去m2的签署相同"

(增加了斜体黑体字)。

而你会把它理解为

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

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

你引用的那一部分实际上是在描述重载。

现在——当考虑超载时——JLS(8.4.9)说:

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

至少从6Java以来,这一直是一致的。这就是overdrid-相当于和重载之间的联系的来源。

好的-所以您的方法会过载,因为它们不是严格重写等价的。正当

错误的

因为就在8.4.8.3中该部分的上面,JLS放入了一个特定的编译时错误:

如果类型声明T具有成员方法m1,并且存在在T中声明的方法m2或T的超类型,并且以下所有内容均为true,则为编译时错误:

>

  • m1和m2同名。

    m2可从T进入。

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

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

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

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

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

  •  类似资料:
    • 我有以下代码,但它不起作用:出现。 我还有这个代码: 这是可行的。在第一种情况下,是,就像在第二种情况下是。那么为什么第一种情况会导致错误,而第二种情况会成功编译? 编辑:我应该怎么做才能实现方法重载,而不引发错误?

    • 问题内容: 我收到此名称冲突错误,我不知道该怎么解决。我有两个类,我正在使用重载的方法“ createSensors”。为了简化,这里是产生问题的代码: 问题答案: 通用解决方案是使用不同的名称。这些方法可以在没有继承关系的类中,因为它们不是实例方法。 如所指出的,问题中的方法实现是相同的(典型值除外)。 (此重载问题经常与擦除运行时类型混淆。重载是链接时而非动态问题,因此可以在语言中轻松解决。这

    • 问题内容: 我明白为什么为两个相等的(通过)对象提供相同的哈希码很重要。但是反之亦然,如果两个对象具有相同的哈希码,它们必须相等吗?合同还存在吗?我找不到一个可能发生这种情况的示例,因为如果所有使用equals方法的属性都被用来覆盖hashcode方法,那么我们将始终对相等的对象使用相同的hashcode。请评论。 问题答案: 如果两个对象相同,则它们不一定相等。否则,您将发现完美的哈希函数。 但

    • 问题内容: 为什么在同一个类中使用以下两种方法是不合法的? 我得到了 方法add(Set)与类型Test中的另一个方法具有相同的擦除add(Set)。 虽然我可以解决它,但我想知道为什么javac不喜欢这样。 我可以看到,在很多情况下,这两种方法的逻辑非常相似,可以用一个方法代替 方法,但并非总是如此。 如果你想让两个带有这些参数,这会特别令人讨厌,因为那样你就不能只更改其中一个的名称。 问题答案

    • 问题内容: Hashcode()和equals()的概念是 1)如果两个对象根据equal()相等,则在这两个对象中的每一个上调用hashcode方法应产生相同的哈希码。 另一个是 2)如果两个对象根据equal()不相等,则不需要在两个对象中的每一个上调用hashcode方法必须产生不同的值。 我尝试并理解了第一个,这是第一点的代码。 上面的程序为两个不同的对象提供了相同的哈希码。 有人可以用一

    • 我有一个采访问题-C#,是否可以在一个类中实现,从接口继承有两个具有相同名称和相同签名的方法?