Java 中的多态性如何处理这种一般情况(带参数的方法)?

Can*_*her 5 java polymorphism

我有一般情况的代码:

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));     
    }
}
Run Code Online (Sandbox Code Playgroud)

结果是:

1--B and A
2--B and A
Run Code Online (Sandbox Code Playgroud)

我知道Java中有一个从高到低的优先级链:

this.show(O), super.show(O), this.show((super)O), super.show((super)O)
Run Code Online (Sandbox Code Playgroud)

我的理解如下:

在这段代码中:

A a = new B()
Run Code Online (Sandbox Code Playgroud)

发生了上行。A 是父类引用,B 是子父类引用。当代码被编译和运行时,子父类引用决定了如何选择方法。在这种情况下,show(A)选择 B 类。

还有一个多态必须满足的要求:选择的方法应该包含在父类定义中。

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

Tii*_*iJ7 4

为了弄清楚为什么会得到B and A两次结果,您需要知道这有两个部分:编译和运行时。

\n

汇编

\n

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

\n
    \n
  1. 查看在 ( ) 上调用该方法的对象a并获取其声明的类型。这种类型是A.
  2. \n
  3. 在类A及其所有超类型中,列出所有名为 的方法show。编译器只会找到show(A). 它不查看B或中的任何方法C
  4. \n
  5. 从找到的方法列表中,选择与参数 ( ) 最匹配的方法(b如果有)。show(A)会接受b,所以选择这个方法。
  6. \n
\n

您经过的第二个调用也会发生同样的情况c。前两步是一样的,第三步又会查找,show(A)因为只有一个,而且也匹配了参数c。因此,对于您的两次通话,其余过程是相同的。

\n

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

\n
invokevirtual org.example.A.show(org.example.A) : java.lang.String [35]\n
Run Code Online (Sandbox Code Playgroud)\n

运行

\n

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

\n
    \n
  1. 获取调用该方法的对象(此时该对象已经在堆栈中),即a
  2. \n
  3. 查看该对象的实际运行时类型。因为a = new B(),这种类型是B
  4. \n
  5. 查找B并尝试找到方法show(A)。由于覆盖了该方法,所以找到了该方法B。如果情况并非如此,它将在超类(AObject)中查找,直到找到这样的方法。值得注意的是,它只考虑show(A)方法,因此例如。show(B)fromB从未被考虑过。
  6. \n
  7. 运行时现在将调用show(A)来自 的方法B,给出String B and A结果。
  8. \n
\n

有关此内容的更多详细信息在规范invokevirtual中给出:

\n
\n

如果解析的方法不是签名多态 (\xc2\xa72.9),则 invokevirtual 指令将按如下方式进行。

\n

设 C 为 objectref 的类。实际要调用的方法是通过以下查找过程选择的:

\n

如果 C 包含覆盖 (\xc2\xa75.4.5) 已解析方法的实例方法 m 的声明,则 m 是要调用的方法,并且查找过程终止。

\n

否则,如果 C 有超类,则使用 C 的直接超类递归执行相同的查找过程;要调用的方法是该查找过程的递归调用的结果。

\n

否则,将引发 AbstractMethodError。

\n
\n

对于您的示例,objectrefis a,其类是B,解析的方法是invokevirtual( show(A)from A)中的方法

\n
\n

tl:dr - 编译时决定调用什么方法,运行时决定从哪里调用它。

\n