gpt4 book ai didi

java - 选择正确的调用方法

转载 作者:行者123 更新时间:2023-11-29 06:13:50 24 4
gpt4 key购买 nike

采取以下代码:

public class Parent
{
public String doIt(Object o) {
return "parent";
}
}

public class Child extends Parent
{
public String doIt(Object s) {
return super.doIt(s) + ": " + "child";
}
}

public class Poly
{
public String makeItHappen() {
Parent p = new Child();
return p.doIt("test");
}
}

由于 Child.doIt() 覆盖 Parent.doIt(),调用 Poly.makeItHappen() 会导致将其打印到控制台:
parent: child

但是,如果我将 Child.doIt() 更改为如下所示:
public String doIt(String s) {
return super.doIt(s) + ": " + "child";
}

现在 Child.doIt() 不会覆盖 Parent.doIt()。当您调用 Poly.makeItHappen() 时,您会得到以下结果:
parent

我对此有点困惑。 p 的编译时类型是 Parent 所以我当然理解它发现 Parent.doIt() 作为一种可能适用的方法,但是,鉴于 p 的运行时类型是 Child,我不确定为什么 Child.doIt( ) 不是。假设它们都被确定为可能适用的方法,我希望 Child.doIt(String) 会在 Parent.doIt(Object) 上被调用,因为它更具体。

我试过咨询 JLS并发现了这一点:

15.12.1 Compile-Time Step 1: Determine Class or Interface to Search

...

In all other cases, the qualified name has the form FieldName . Identifier; then the name of the method is the Identifier and the class or interface to search is the declared type T of the field named by the FieldName, if T is a class or interface type, or the upper bound of T if T is a type variable.



对我来说,这表示将使用 p 的编译时类型。当我看到 Parent.doIt(Object) 被调用而 Child.doIt(String) 没有被调用时,这是有道理的。但就 Child.doIt() 正确覆盖 Parent.doIt() 时注意到的多态行为而言,这没有任何意义 - 在这种情况下,正在分析 Parent 和 Child 的两种方法以找到可能适用的方法,所以为什么不在第二种情况下,也是?

我知道我在这里遗漏了一些东西,但我无法弄清楚为什么我会看到我的行为。如果有人能对此有所了解,我将不胜感激。

编辑:找到答案:

感谢 Jack 的回复,我能够在 JLS 中找到答案。我之前提到的 JLS 部分实际上侧重于在编译时找到正确的方法,并没有涵盖在运行时调用正确方法的过程。可以在 JLS 的标题为 15.12.4 Runtime Evaluation of Method Invocation 的部分中找到该过程的部分。 .

在那里,我发现了这段文字:

Otherwise, the invocation mode is interface, virtual, or super, and overriding may occur. A dynamic method lookup is used. The dynamic lookup process starts from a class S, determined as follows:



我的调用模式是虚拟的,所以上面的语句适用...

If the invocation mode is interface or virtual, then S is initially the actual run-time class R of the target object.

...

The dynamic method lookup uses the following procedure to search class S, and then the superclasses of class S, as necessary, for method m.



好的,所以这对我来说似乎很奇怪。据此,JVM 将开始在 Child 中寻找一个适用的方法来调用,这会让我相信 Child.doIt(String) 会被调用。但是,继续阅读...

Let X be the compile-time type of the target reference of the method invocation.

...

If class S contains a declaration for a non-abstract method named m with the same descriptor (same number of parameters, the same parameter types, and the same return type) required by the method invocation as determined at compile time (§15.12.3), then:



类“S”,即 Child,确实包含一个方法,该方法的描述符与编译时确定的方法调用相同(毕竟,字符串“是一个”对象,所以描述符是相同的)。似乎仍然应该调用 Child.doIt(String) ,但请继续阅读......

If the invocation mode is virtual, and the declaration in S overrides (§8.4.8.1) X.m, then the method declared in S is the method to be invoked, and the procedure terminates.

...

Otherwise, if S has a superclass, this same lookup procedure is performed recursively using the direct superclass of S in place of S; the method to be invoked is the result of the recursive invocation of this lookup procedure.



粗体字是其中非常重要的部分。正如我所提到的,当我更改 Child.doIt() 方法时,它不再覆盖 Parent 的 doIt() 方法。因此,即使 JVM 正在评估 Child.doIt() 方法作为调用的潜在候选者,它也无法被调用,因为它没有覆盖 X 中定义的方法,即 Parent。我真的被挂断了,因为我认为 JVM 甚至没有检查 Child.doIt 作为一种可能适用的方法,而且这似乎不正确。现在我相信JVM 将该方法检查为可能适用的方法,但随后忽略它,因为它没有正确覆盖父方法。在这种情况下,我认为子类中的方法会被调用,不是因为它覆盖了父类方法,而是因为它是最具体的。然而,在这种情况下,情况并非如此。

JLS 中的下一行简单地解释了这个过程在父类(super class)上递归执行,导致 Parent.doIt(Object) 被调用。

直观地说,这对我来说完全有道理,但我无法理解 JVM 是如何实际执行这个过程的。当然,查看 JLS 的正确部分会有很大帮助。

最佳答案

方法的动态绑定(bind)在运行时适用于具有相同签名的不同实现之间,但是当编译器查看签名时,所有可能的方法的集合都是静态确定的。由于您声明 p作为 Parent类,在运行时它将查找 Parent 中存在的方法类,并且,如果存在更具体的实现(由于子类,如您的示例中),那么它将被选择而不是祖先。

由于您的方法不会覆盖任何内容,因此在编译时它将选择不同的签名(带有 Object 的签名)并忽略它。由于属性的类型 p,它在运行时不会显示为匹配的可能性.

关于java - 选择正确的调用方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/5822960/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com