在types中,多态的主要原理是将“什么与谁”脱钩,但让我困惑的是,方法调用机制是如何在多态性中发现和调用正确的方法体的。
因为在java中,所有方法绑定都是late-binding,除非方法是static、final或private,而后期绑定则由JVM完成,它为每个类预计算method table,然后在运行时在正常的方法调用中查找表。
但在多态过程中也会发生同样的事情。例如
假设我有一个带有Cycle方法的泛型类ride()
class Cycle {
public void ride(){
System.out.println("I'm Riding generic Cycle()");
}
}我有三个专门的类Bicycle、Tricycle和Unicycle,它们扩展了泛型类Cycle并重写了它的ride()方法。
class Bicycle extends Cycle {
public void ride() {
System.out.println("I'm riding Bicycle");
}
}
class Tricycle extends Cycle{
public void ride() {
System.out.println("I'm riding Tricycle ");
}
}
class Unicycle extends Cycle {
public void ride() {
System.out.println("I'm Riding Unicycle ");
}
}这是测试上述多态性的TestRide类。
public class TestRide {
public static void ride(Cycle c){
c.ride();
}
public static void main(String[] args){
Cycle Cycling = new Cycle();
ride(Cycling);
Bicycle bi = new Bicycle();
ride(bi);
Tricycle tri = new Tricycle();
ride(tri);
Unicycle uni = new Unicycle();
ride(uni);
}
}输出是
I'm Riding generic Cycle()
I'm riding Bicycle
I'm riding Tricycle
I'm Riding Unicycle 字节码:
public static void main(java.lang.String[]);
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=5, args_size=1
0: new #17 // class com/polymorphism/Cycle
3: dup
4: invokespecial #24 // Method com/polymorphism/Cycle."
<init>":()V
7: astore_1
8: aload_1
9: invokestatic #25 // Method ride:(Lcom/polymorphism/
Cycle;)V
12: new #27 // class com/polymorphism/Bicycle
15: dup
16: invokespecial #29 // Method com/polymorphism/Bicycle
."<init>":()V
19: astore_2
20: aload_2
21: invokestatic #25 // Method ride:(Lcom/polymorphism/
Cycle;)V
24: new #30 // class com/polymorphism/Tricycle
27: dup
28: invokespecial #32 // Method com/polymorphism/Tricycl
e."<init>":()V
31: astore_3
32: aload_3
33: invokestatic #25 // Method ride:(Lcom/polymorphism/
Cycle;)V
36: new #33 // class com/polymorphism/Unicycle
39: dup
40: invokespecial #35 // Method com/polymorphism/Unicycl
e."<init>":()V
43: astore 4
45: aload 4
47: invokestatic #25 // Method ride:(Lcom/polymorphism/
Cycle;)V
50: return即使在字节码中,它也像往常一样对invokestatic和invokespecial进行了方法调用,尽管我认为它会使用invokedynamic来确定适合对象实际类型的方法的版本。但事实并非如此。
那么,当我们只在ride()方法中传递一个向上的对象(如ride(bi)中的TestRide类)时,Java如何在多态性期间计算出实际的方法调用呢?
编辑:乘坐方法ByteCode
public static void ride(com.polymorphism.Cycle);
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: invokevirtual #16 // Method com/polymorphism/Cycle.r
ide:()V
4: return发布于 2015-09-20 15:38:02
首先,invokedynamic是针对Java8lambda和非Java代码的,所以您可以忘记这一点。
除此之外,还有四个调用指令(invokespecial、invokestatic、invokevirtual和invokeinterface)。您可以在JVM分隔中看到精确的语义,但底线是invokevirtual和invokeinterface是虚拟方法调用,即在运行时根据目标的conrete类型选择实际调用的方法。
代码中唯一的虚拟调用是在TestRide.ride中。列出的目标是Cycle.ride:()V。但是,由于它是一个虚拟调用,JVM将在运行时检查第一个参数的实际类型,并调用该方法的最派生版本。
这与C++中的虚拟方法调用类似,只是JVM和JIT编译的抽象允许进行更优化的实现。
还请注意,这不应与方法重载混淆,方法重载是编译时多态的一种形式。对于重载的方法,编译器根据参数的编译时间类型选择要调用的方法。
发布于 2015-09-20 12:04:26
我认为@JBNizet已经在评论中找到了解决方案(我的猜测是错误的)。但既然他不把它作为答案,我会这么做的:
main方法不应该显示任何动态行为,因为它总是会调用单个方法TestRide.ride(Cycle c)。没有其他可能的方法,所以没有什么需要解决的。
动态方法调用在该方法TestRide.ride(Cycle c)中。现在您已经发布了这些代码,实际上,我们看到了使用invokevirtual的动态方法分派。所以,毕竟,没有惊喜。动态方法调度就在那里。
https://stackoverflow.com/questions/32678928
复制相似问题