33

我一直在学习,因为我有一个考试,我对大多数 Java 没有太多问题,但我偶然发现了一个我无法解释的规则。这是一个代码片段:

public class A {

    public int method(Object o) {
        return 1;
    }

    public int method(A a) {
        return 2;
    }
}

public class AX extends A {

    public int method(A a) {
        return 3;
    }

    public int method(AX ax) {
        return 4;
    }
}

public static void main(String[] args) {
    Object o = new A();
    A a1 = new A();
    A a2 = new AX();
    AX ax = new AX();

    System.out.println(a1.method(o));
    System.out.println(a2.method(a1));
    System.out.println(a2.method(o));
    System.out.println(a2.method(ax));
}

这将返回:

1 3 1 3

虽然我希望它会返回:

1 3 1 4

为什么 a2 的类型决定了 AX 中调用哪个方法?

我一直在阅读有关重载规则和继承的内容,但这似乎很模糊,以至于我无法找到确切的规则。任何帮助将不胜感激。

4

3 回答 3

32

这些方法调用的行为由Java 语言规范(参考第 8.4.9 节)规定和描述。

调用方法时(第 15.12 节),在编译时使用实际参数(和任何显式类型参数)的数量和参数的编译时类型来确定将被调用的方法的签名( §15.12.2)。如果要调用的方法是实例方法,则要调用的实际方法将在运行时使用动态方法查找(第 15.12.4 节)确定。

在您的示例中,Java 编译器确定您正在调用方法的实例的编译类型最接近的匹配项。在这种情况下:

A.method(AX)

最接近的方法来自类型 A,带有签名A.method(A)。在运行时,动态调度是在 A 的实际类型(它是 AX 的一个实例)上执行的,因此这是实际调用的方法:

AX.method(A)
于 2012-06-19T23:18:59.663 回答
4

我将以更简单的方式澄清它。看看你何时使用超类引用制作子类对象,就像你在这里所做的那样。

始终记住一件事,当您使用超类引用调用时,无论对象是否属于子类,它都会转到超类,检查具有此名称的方法以及正确的签名是否存在。

现在如果它会找到它,它会检查它是否被覆盖?如果是的话,它将像这里那样转到子类方法。另一个明智的做法是执行相同的超类方法。

我可以给你举个例子……只是隐藏

public int method(A a) {
        return 3;
    }

方法并检查您的答案,您将得到 1 2 1 2,为什么是因为它优先考虑参考。因为你覆盖了它而不是调用它,所以它给了 3..!! 希望它很大但很容易理解。快乐学习

于 2014-04-15T08:07:10.157 回答
1

a2 被引用为 A 并且 JVM 首先使用引用(而不是您期望的实际对象)。

于 2012-06-19T23:19:40.557 回答