简体   繁体   English

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

[英]How does Java Determine methods call at runtime in polymorphism?

While the main principle of polymorphism is decoupling "what from who" in term of types , but what confuses me how does method-call mechanism finds out and calls the correct method body in polymorphism. 虽然多态性的主要原则是在types “什么与谁”分离,但令我困惑的是方法调用机制如何找出并在多态中调用正确的方法体。

Since in java all method binding is late-binding unless the method is static , final or private , and late-binding is done by JVM which precomputes method table for each class and then do a table look up during runtime in normal method call. 因为在java中,所有方法绑定都是late-binding除非该方法是staticfinalprivate ,并且后期绑定由JVM完成,JVM预先计算每个类的method table ,然后在运行时在正常方法调用中查找表。

But the same thing happens during polymorphism too. 但同样的事情也发生在多态性中。 For example 例如

Suppose I've a Generic class Cycle with a ride() method 假设我有一个带有ride()方法的Generic class Cycle

class Cycle {

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

}

And i have three Specialized Class Bicycle Tricycle and Unicycle which extends Generic class Cycle and overrides its ride() method. 我有三个Specialized Class Bicycle TricycleUnicycle ,它扩展了Generic class 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 ");

    }

}

This is the TestRide class to Test the above Polymorphism. 这是测试上述多态性的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);
    }

}

The Output is 输出是

I'm Riding generic Cycle()
I'm riding Bicycle
I'm riding Tricycle 
I'm Riding Unicycle 

Byte Code: 字节代码:

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

Even in the bytecode its just as usual method call with invokestatic and invokespecial while i thought it would use invokedynamic to figure out the version of the method that is appropriate for the actual type of the object. 即使在字节码中,它就像通常的方法一样调用invokestaticinvokespecial而我认为它会使用invokedynamic来找出适合于对象实际类型的方法版本。 But that was not the case. 但事实并非如此。

So how does Java figure out the actual method call during polymorphism while we just pass an upcasted Object in the ride() method like ride(bi) in TestRide class ? 那么,如何的Java找出多态性在实际的方法调用,而我们只是传递一个upcasted对象在ride()像法ride(bi)TestRide类?

EDIT: RIDE method ByteCode 编辑:RIDE方法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

First off invokedynamic is for Java 8 lambdas and non-Java code, so you can forget about that. 首先, invokedynamic用于Java 8 lambdas和非Java代码,因此您可以忘记这一点。

Apart from that, there are four invoke instructions ( invokespecial , invokestatic , invokevirtual , and invokeinterface ). 除此之外,有四个调用指令( invokespecialinvokestaticinvokevirtualinvokeinterface )。 You can see the precise semantics in the JVM sepcification, but the bottom line is that invokevirtual and invokeinterface are virtual method calls, ie the actual method called is chosen at runtime based on the conrete type of the target. 你可以看到在JVM规格说明的精确语义,但底线是, invokevirtualinvokeinterface方法调用,即在运行时根据conrete类型目标的选择所谓的实际方法。

The only virtual call in your code is in TestRide.ride. 代码中唯一的虚拟调用是在TestRide.ride中。 The listed target is Cycle.ride:()V . 列出的目标是Cycle.ride:()V However, since it is a virtual call, the JVM will check the actual type of the first argument at runtime and call the most derived version of that method. 但是,由于它是虚拟调用,因此JVM将在运行时检查第一个参数的实际类型,并调用该方法的派生程度最高的版本。

This is similar to virtual method calls in C++, except that the abstraction of the JVM and JIT compilation allows the potential for more optimized implementations. 这与C ++中的虚方法调用类似,不同之处在于JVM和JIT编译的抽象允许更多优化实现的可能性。

Also note that this is not to be confused with method overloading, which is a form of compile-time polymorphism. 另请注意,这不应与方法重载混淆,方法重载是编译时多态的一种形式。 For overloaded methods, the compiler chooses which one to call based on the compile time type of the arguments. 对于重载方法,编译器根据参数的编译时类型选择调用哪一个。

I think @JBNizet figured out the solution already in the comments (and my guess turned out to be wrong). 我认为@JBNizet已经在评论中找到了解决方案(我的猜测结果证明是错误的)。 But since he doesn't post it as an answer, I'll do it: 但由于他没有将其作为答案发布,我会这样做:

The main method isn't supposed to show any dynamic behavior because it is always ever going to call that one single method TestRide.ride(Cycle c) . main是不应该的方法来显示任何动态的行为,因为它总是不断要调用一个 单一方法TestRide.ride(Cycle c) There is no other possible method, so nothing to figure out. 没有其他可能的方法,所以没有什么可以搞清楚的。

The dynamic method call is inside that method TestRide.ride(Cycle c) . 动态方法调用在该方法TestRide.ride(Cycle c) And now that you posted that code, indeed we see a dynamic method dispatch using invokevirtual . 现在您发布了该代码,我们确实看到了使用invokevirtual的动态方法调度。 So, after all, no surprises. 所以,毕竟没有惊喜。 The dynamic method dispatch is there. 动态方法调度就在那里。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM