有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

java选择正确的调用方法

以下面的代码为例:

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");
    }
}

因为那个孩子。doIt()覆盖父级。doIt(),调用Poly。makeItHappen()会将其打印到控制台:

parent: child

然而,如果我在孩子身上做出改变。doIt()看起来像这样:

public String doIt(String s) {
    return super.doIt(s) + ": " + "child";
}

现在,孩子。doIt()不重写父项。doIt()。当你调用Poly时。makeItHappen(),您将得到以下结果:

parent

我对此有点困惑。p的编译时类型是Parent,所以我当然理解它。doIt()是一个潜在的适用方法,但是,鉴于p的运行时类型是Child,我不确定为什么是Child。doIt()不是。假设这两种方法都被认为是潜在的适用方法,我希望是Child。要通过父级调用的doIt(字符串)。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。当我看到父母时,这是有道理的。doIt(对象)在子对象时被调用。doIt(String)不是。但从孩子出生时注意到的多态性行为来看,这是没有意义的。doIt()正确覆盖父级。doIt()-在这种情况下,父母和孩子的两种方法都在被分析,以找到潜在的适用方法,那么为什么不在第二种情况下也进行分析呢

我知道我错过了一些东西,但我就是不明白为什么我会看到自己的行为。如果有人能对此有所了解,我将不胜感激

编辑:找到答案:

多亏了杰克的回答,我才能够在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”将是子类,它确实包含一个方法,该方法的描述符与编译时确定的方法调用相同(毕竟字符串“是”对象,所以描述符是相同的)。它看起来仍然像个孩子。应该调用doIt(字符串),但继续阅读

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.

加粗的部分才是真正重要的部分。正如我提到的,当我改变方法时,孩子们。doIt(),它不再从父级重写doIt()方法。因此,即使JVM正在评估子代。doIt()方法作为调用的潜在候选者,它无法被调用,因为它不重写在父级X中定义的方法。我真的被挂断了,因为我认为JVM甚至不是check Child。doIt是一种潜在的适用方法,但这似乎并不正确。现在我相信JVM正在检查该方法是否是一个潜在的适用方法,但是忽略了它,因为它没有正确地覆盖父方法。在这种情况下,我认为会调用子类中的方法,不是因为它重写父类方法,而是因为它是最具体的。然而,在这种情况下,情况并非如此

JLS中的下一行简单地解释了这个过程是在超类上递归执行的,主要是父类。要调用的doIt(对象)

直觉上,这对我来说是完全有意义的,但我就是不知道JVM实际上是如何执行这个过程的。当然,查看JLS的正确部分会有很大帮助


共 (0) 个答案