首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Java如何确定方法在运行时的多态调用?

Java如何确定方法在运行时的多态调用?
EN

Stack Overflow用户
提问于 2015-09-20 11:36:14
回答 2查看 2.3K关注 0票数 6

types中,多态的主要原理是将“什么与谁”脱钩,但让我困惑的是,方法调用机制是如何在多态性中发现和调用正确的方法体的。

因为在java中,所有方法绑定都是late-binding,除非方法是staticfinalprivate,而后期绑定则由JVM完成,它为每个类预计算method table,然后在运行时在正常的方法调用中查找表。

但在多态过程中也会发生同样的事情。例如

假设我有一个带有Cycle方法的泛型类ride()

代码语言:javascript
复制
class Cycle {

    public void ride(){
        System.out.println("I'm Riding generic Cycle()");
    }

}

我有三个专门的类BicycleTricycleUnicycle,它们扩展了泛型类Cycle并重写了它的ride()方法。

代码语言:javascript
复制
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类。

代码语言:javascript
复制
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);
    }

}

输出是

代码语言:javascript
复制
I'm Riding generic Cycle()
I'm riding Bicycle
I'm riding Tricycle 
I'm Riding Unicycle 

字节码:

代码语言:javascript
复制
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

即使在字节码中,它也像往常一样对invokestaticinvokespecial进行了方法调用,尽管我认为它会使用invokedynamic来确定适合对象实际类型的方法的版本。但事实并非如此。

那么,当我们只在ride()方法中传递一个向上的对象(如ride(bi)中的TestRide类)时,Java如何在多态性期间计算出实际的方法调用呢?

编辑:乘坐方法ByteCode

代码语言:javascript
复制
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
EN

回答 2

Stack Overflow用户

发布于 2015-09-20 15:38:02

首先,invokedynamic是针对Java8lambda和非Java代码的,所以您可以忘记这一点。

除此之外,还有四个调用指令(invokespecialinvokestaticinvokevirtualinvokeinterface)。您可以在JVM分隔中看到精确的语义,但底线是invokevirtualinvokeinterface是虚拟方法调用,即在运行时根据目标的conrete类型选择实际调用的方法。

代码中唯一的虚拟调用是在TestRide.ride中。列出的目标是Cycle.ride:()V。但是,由于它是一个虚拟调用,JVM将在运行时检查第一个参数的实际类型,并调用该方法的最派生版本。

这与C++中的虚拟方法调用类似,只是JVM和JIT编译的抽象允许进行更优化的实现。

还请注意,这不应与方法重载混淆,方法重载是编译时多态的一种形式。对于重载的方法,编译器根据参数的编译时间类型选择要调用的方法。

票数 1
EN

Stack Overflow用户

发布于 2015-09-20 12:04:26

我认为@JBNizet已经在评论中找到了解决方案(我的猜测是错误的)。但既然他不把它作为答案,我会这么做的:

main方法不应该显示任何动态行为,因为它总是会调用单个方法TestRide.ride(Cycle c)。没有其他可能的方法,所以没有什么需要解决的。

动态方法调用在该方法TestRide.ride(Cycle c)中。现在您已经发布了这些代码,实际上,我们看到了使用invokevirtual的动态方法分派。所以,毕竟,没有惊喜。动态方法调度就在那里。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/32678928

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档