Java中的多态如何在这种一般情况下起作用(带有参数的方法)? [英] How does polymorphism in Java work for this general case (method with parameter)?

查看:103
本文介绍了Java中的多态如何在这种一般情况下起作用(带有参数的方法)?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个一般情况的代码:

I have the code for a general case:

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中有一个从高到低的优先级链:

I know there is a priority chain from high to low in Java:

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

我的理解如下:

在此代码中:

A a = new B()

发生上流. A是父类参考,而B是子父类参考.编译并运行代码后,子父类引用将确定如何选择方法.在这种情况下,将选择B类中的show(A).

An upcast happens. A is a parent class reference and B is a child parent class reference. When the code is compiled and run, the child parent class reference determines how the method is chosen. In this case, show(A) in class B is chosen.

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

There is also a requirement that polymorphism has to meet: The method that is chosen should be included in the parent class definition.

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

Could someone give a more detailed explanation on the result shown?

推荐答案

为了弄清为什么两次获得结果B and A的原因,您需要知道有两部分:编译和运行时.

In order to get why you get the result B and A twice, you need to know that there are 2 parts to this: compilation and runtime.

编译

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

When encountering the statement a.show(b), the compiler takes these basic steps:

  1. 查看在其上调用了该方法的对象(a),并获取其声明的类型.此类型为A.
  2. 在类A及其所有超级类型中,列出所有名为show的方法.编译器将仅找到show(A).它不会查看BC中的任何方法.
  3. 从找到的方法列表中,选择与参数(b)最匹配的方法. show(A)将接受b,因此选择了此方法.
  1. Look at the object that the method is called on (a) and get its declared type. This type is A.
  2. In class A and all of its super types, make a list of all methods that are named show. The compiler will find only show(A). It does not look at any methods in B or C.
  3. From the list of found methods, choose the one that best matches the parameter (b) if any. show(A) will accept b, so this method is chosen.

您通过c的第二个呼叫也会发生相同的情况.前两个步骤相同,第三步将再次找到show(A),因为只有一个,并且它还与参数c相匹配.因此,对于您的两个呼叫,其余过程都是相同的.

The same thing will happen for the second call where you pass c. The first two steps are the same, and the third step will again find show(A) since there is only one, and it also matches the parameter c. So, for both of your calls, the rest of the process is the same.

一旦编译器确定了所需的方法,它将创建字节码指令invokevirtual,并将已解析的方法show(A)设置为应调用的方法(如Eclipse中通过打开):

Once the compiler has figured out what method it needs, it will create a byte-code instruction invokevirtual, and put the resolved method show(A) as the one it should call (as shown in Eclipse by opening the .class):

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

运行时

运行时(最终到达invokevirtual时)还需要执行一些步骤.

The runtime, when it eventually gets to the invokevirtual needs to do a few steps also.

  1. 获取调用该方法的对象(当时该对象已经在堆栈中),即a.
  2. 查看此对象的实际运行时类型.从a = new B()开始,此类型为B.
  3. 查找B并尝试找到方法show(A).找到此方法,因为B将其覆盖.如果不是这种情况,它将在超类(AObject)中查找,直到找到这样的方法为止.重要的是要注意,它仅考虑show(A)方法,例如.永远不会考虑B中的show(B).
  4. 运行时现在将从B调用方法show(A),结果为String B and A.
  1. Get the object on which the method is called (which is already on the stack by then), which is a.
  2. Look at the actual runtime type of this object. Since a = new B(), this type is B.
  3. Look in B and try to find the method show(A). This method is found since B overrides it. If this had not been the case, it would look in the super classes (A and Object) until such a method is found. It is important to note that it only considers show(A) methods, so eg. show(B) from B is never considered.
  4. The runtime will now call method show(A) from B, giving the String B and A as result.

有关更多详细信息,请参见

More detail about this is given in the spec for invokevirtual:

如果解析的方法不是签名多态的(第2.9节),那么invokevirtual指令将按以下步骤进行.

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

让C成为objectref的类.通过以下查找过程选择要调用的实际方法:

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

如果C包含一个实例方法m的声明,该声明将覆盖已解析的方法(第5.4.5节),则m是要调用的方法,并且查找过程终止.

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.

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

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.

否则,将引发AbstractMethodError.

Otherwise, an AbstractMethodError is raised.

在您的示例中,objectrefa,其类是B,并且已解析的方法是invokevirtual中的方法(A中的show(A))

For your example, the objectref is a, its class is B and the resolved method is the one from the invokevirtual (show(A) from A)

tl:dr-编译时确定要调用的方法,运行时确定从何处调用该方法.

tl:dr - Compile-time determines what method to call, runtime determines where to call it from.

这篇关于Java中的多态如何在这种一般情况下起作用(带有参数的方法)?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆