在像Java这样的静态类型语言中动态方法解析背后的原因是什么

时间:2017-06-26 13:09:36

标签: java override

对于参考变量的动态/静态类型和Java中的动态方法解析,我有点困惑,我猜这个概念。

考虑:

public class Types {

    @Override
    public boolean equals(Object obj){
        System.out.println("in class Types equals()");
        return false;//Shut-up compiler!
    }

    public static void main(String[] args){
        Object typ = new Types();
        typ.equals("Hi");//can do this as String is a subclass of Object
    }
}

首先:引用变量typ属于类型,不是吗?!

那么为什么选择静态类型对象和动态类型类型进行方法重写后,是什么原因?

第二:编译器是否有足够的信息来调用正确的equals()?

如果类类型没有重写的equals(),那么它可以调用Object.equals()方法。

在这种情况下,类类型可以,并且编译器知道它。

为什么这不能像重载那样早期绑定?为什么要把它留给JVM?

3 个答案:

答案 0 :(得分:5)

这是面向对象编程的必要基石。

归结为多态。有关详细信息,请参阅here

您绝对希望子类可以覆盖行为;因为这可以让你保持客户代码不变;但你仍然能够引入新的/不同的行为;只需将另一个对象传递给该客户端代码即可。

客户端代码知道要调用哪个方法;但 dispatch 必须在运行时发生。只是因为编译器可以知道(大多数情况下)传入参数的完全类型。

答案 1 :(得分:3)

示例中的引用变量typ的类型为Object

它引用的对象是Types类型。

编译器没有足够的信息知道该对象实际上是Types并使用Type.equals(Object)

这可能令人困惑。如果你写

Object typ = new Types();

然后,编译器肯定知道typ中的内容是Types。它只是将这些信息编译成代码!

但是,如果您决定将该行更改为

,该怎么办?
Object type = new ObjectFactory().choose(choice).use(decision).build();

好吧,现在你实际上并不知道build会想出什么。只有在ObjectFactory决定如何处理choicedecision的值后,才能在运行时知道这一点。

所以在这种情况下,编译器不知道。唯一可用的信息是变量的静态类型。如果使用new时编译器的行为与使用上述工厂时的编译器表现不同,那将是非常糟糕的。

现在看看这个场景:

public class Types {
     public boolean equals( Object obj ) {
        // something
     }

     public boolean equals( String str ) {
        return false;
     }
}

public class Main {

    public static void main(String[] args) {
       Object typ = new Types();
       System.out.println( typ.equals("foo" ) );
    }
}

现在,在这种情况下,TypesMain是两个独立的编译单元(不同的文件等)。假设编译器决定根据typTypes的事实。然后它将使用Types.equals(String)

但现在您可以修改Types,删除equals(String),然后重新编译Types。当您运行该程序时,您将得到“没有此类方法异常”,因为编译器假定Types具有equals(String)方法。所以你的程序失败了,尽管它是一个完全合法的 Java程序,尽管在新equals(Object)中有一个正确的匹配方法Types

因此,真正的编译器使用静态类型来确定方法是否存在并且对于给定的静态参数是合法的,并且仅在运行时,JVM将能够找到实际类型的内容,并调用适用的覆盖那种方法。

答案 2 :(得分:0)

编译器只知道vriable的Static Type,并用它来检查java中的某些约束。

拳:

  

参考变量typ的类型是Type,不是吗?!

我认为编译器认为变量typObject,当然typ真正指向Types对象,因为每个类都是{{{}的子类型1}},因此它可以分配给变量Object

第二

  

编译器是否有足够的信息来调用正确的equals()?

不。编译器只知道程序调用变量typ的方法equals(),但它不知道typ指向typ实例或其他类型实例。所以在编译时不能确定调用真正的方法是什么。

查看以下代码:

Types

并查看JVM中的指令集:

    Object typ = new Main();
    Object o=new Object();
    typ.equals("Hi");//can do this as String is a subclass of Object
    o.equals("Hi");

注意

16: aload_1 17: ldc #42 // String Hi 19: invokevirtual #43 // Method java/lang/Object.equals:(Ljava/lang/Object;)Z 22: pop 23: aload_2 24: ldc #42 // String Hi 26: invokevirtual #43 // Method java/lang/Object.equals:(Ljava/lang/Object;)Z

19: invokevirtual #43 // Method java/lang/Object.equals (Ljava/lang/Object;)Z

26: invokevirtual #43 // Method java/lang/Object.equals:(Ljava/lang/Object;)Ztyp.equals("Hi")编译相同的指令集。但是要调用哪个方法取决于实例的方法引用表。

o.equals("Hi")

#43 = Methodref #41.#132 // java/lang/Object.equals:(Ljava/lang/Object;)Z 是一个指向真实方法的方法引用.jvm中的每个实例都有一个方法引用表。如果类#43有一些方法的继承方法,那么方法引用将是已更改为Override方法。