Java中的多态如何适用于这种一般情况(带参数的方法)?

我有一般情况的代码:

public class A {
    public String show(A obj) {
        return ("A and A");
    }
}

public class B extends A {
    public String show(B obj) {
        return ("B and B");
    }

    public String show(A obj) {
        return ("B and A");
    }
}

public class C extends B {

}

public class Test {
    public static void main(String[] args) {
        A a = new B();
        B b = new B();
        C c = new C();

        System.out.println("1--" + a.show(b));
        System.out.println("2--" + a.show(c));     
    }
}

结果是:

1--B and A
2--B and A

我知道Java中存在从高到低的优先级链:

this.show(O), super.show(O), this.show((super)O), super.show((super)O)

我的理解如下:

在这段代码中:

A a = new B()

发生了一场*. A是父类引用,B是子父类引用.编译并运行代码时,子父类引用确定如何选择方法.在这种情况下,选择B类中的show(A).

还需要多态性必须满足:
选择的方法应包含在父类定义中.

有人可以对显示的结果做出更详细的解释吗?

解决方法:

为了得到结果B和A两次的原因,你需要知道这有两个部分:编译和运行时.

汇编

遇到语句a.show(b)时,编译器会采取以下基本步骤:

>查看在(a)上调用该方法的对象并获取其声明的类型.这种类型是A.
>在A类及其所有超类型中,列出名为show的所有方法.编译器只会找到show(A).它没有看B或C中的任何方法.
>从找到的方法列表中,选择与参数(b)最匹配的方法(如果有). show(A)将接受b,因此选择此方法.

传递c的第二个调用也会发生同样的事情.前两个步骤是相同的​​,第三步将再次找到show(A),因为只有一个,它也匹配参数c.因此,对于您的两个调用,其余过程都是相同的.

一旦编译器弄清楚它需要什么方法,它将创建一个字节码指令invokevirtual,并将解析后的方法show(A)作为它应该调用的方法(如Eclipse中所示,打开.class):

invokevirtual org.example.A.show(org.example.A) : java.lang.String [35]

运行

运行时,当它最终到达invokevirtual时,还需要执行几个步骤.

>获取调用该方法的对象(此时已经在堆栈中),这是一个.
>查看此对象的实际运行时类型.由于a = new B(),此类型为B.
>查看B并尝试找到方法show(A).由于B覆盖它,因此找到此方法.如果不是这种情况,它会查找超类(A和Object),直到找到这样的方法.重要的是要注意它只考虑show(A)方法,例如.从未考虑过来自B的节目(B).
>运行时现在将从B调用方法show(A),给出字符串B和A作为结果.

关于此的更多细节在spec for invokevirtual中给出:

If the resolved method is not signature polymorphic (§2.9), then the invokevirtual instruction proceeds as follows.

Let C be the class of objectref. The actual method to be invoked is selected by the following lookup procedure:

If C contains a declaration for an instance method m that overrides (§5.4.5) the resolved method, then m is the method to be invoked, and the lookup procedure terminates.

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

Otherwise, an AbstractMethodError is raised.

对于您的示例,objectref是a,其类是B,并且已解析的方法是来自invokevirtual的方法(显示(A)来自A)

tl:dr – 编译时确定要调用的方法,运行时确定从哪里调用它.

上一篇:带有通用类的Java枚举


下一篇:Java多态性和方法链接