Java中的多态如何在这种一般情况下起作用(带有参数的方法)? [英] How does polymorphism in Java work for this general case (method with parameter)?
问题描述
我有一个一般情况的代码:
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:
- 查看在其上调用了该方法的对象(
a
),并获取其声明的类型.此类型为A
. - 在类
A
及其所有超级类型中,列出所有名为show
的方法.编译器将仅找到show(A)
.它不会查看B
或C
中的任何方法. - 从找到的方法列表中,选择与参数(
b
)最匹配的方法.show(A)
将接受b
,因此选择了此方法.
- Look at the object that the method is called on (
a
) and get its declared type. This type isA
. - In class
A
and all of its super types, make a list of all methods that are namedshow
. The compiler will find onlyshow(A)
. It does not look at any methods inB
orC
. - From the list of found methods, choose the one that best matches the parameter (
b
) if any.show(A)
will acceptb
, 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.
- 获取调用该方法的对象(当时该对象已经在堆栈中),即
a
. - 查看此对象的实际运行时类型.从
a = new B()
开始,此类型为B
. - 查找
B
并尝试找到方法show(A)
.找到此方法,因为B
将其覆盖.如果不是这种情况,它将在超类(A
和Object
)中查找,直到找到这样的方法为止.重要的是要注意,它仅考虑show(A)
方法,例如.永远不会考虑B
中的show(B)
. - 运行时现在将从
B
调用方法show(A)
,结果为String
B and A
.
- Get the object on which the method is called (which is already on the stack by then), which is
a
. - Look at the actual runtime type of this object. Since
a = new B()
, this type isB
. - Look in
B
and try to find the methodshow(A)
. This method is found sinceB
overrides it. If this had not been the case, it would look in the super classes (A
andObject
) until such a method is found. It is important to note that it only considersshow(A)
methods, so eg.show(B)
fromB
is never considered. - The runtime will now call method
show(A)
fromB
, giving theString
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.
在您的示例中,objectref
是a
,其类是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屋!