在像Java这样的静态类型语言中,动态方法解析背后的原因是什么
我想我对Java中引用变量的动态/静态类型和动态方法解析的概念有点困惑 考虑:在像Java这样的静态类型语言中,动态方法解析背后的原因是什么,java,overriding,Java,Overriding,我想我对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(Stri
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
是类型,不是吗
那么,typ具有用于方法重写的静态类型对象和动态类型类型背后的原因是什么
第二:编译器没有足够的信息来调用正确的equals()
如果类类型没有重写的equals(),那么它可以调用Object.equals()
方法
在这种情况下,类类型会这样做,编译器也知道这一点
为什么这不能像重载一样是早期绑定?为什么要把它留给JVM呢?这是面向对象编程的基础 它归结为多态性。请参阅以获取进一步阅读 您绝对希望子类可以覆盖行为;因为这允许您保持客户端代码不变;但你仍然能够引入新的/不同的行为;只需将另一个对象传递给该客户机代码
客户端代码知道调用哪个方法;但调度必须在运行时进行。这仅仅是因为编译器不知道(在大多数情况下)传入参数的确切类型。示例中的引用变量
typ
属于Object
类型
它引用的对象的类型为类型
编译器没有足够的信息来知道对象实际上是Types
,并且使用Type.equals(object)
这可能令人困惑。如果你写信
Object typ = new Types();
那么编译器肯定知道typ
中包含的是类型。它只是将这些信息编译成代码
但是,如果您决定将该行更改为
Object type = new ObjectFactory().choose(choice).use(decision).build();
好吧,现在您实际上不知道构建将产生什么。只有在ObjectFactory决定如何处理choice
和decision
的值之后,才会在运行时知道这一点
所以在这种情况下,编译器不知道。唯一可用的信息是变量的静态类型。如果使用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" ) );
}
}
现在,在本例中,Types
和Main
是两个独立的编译单元(不同的文件等)。假设编译器决定根据typ
是Types
这一事实进行编译。然后它将使用类型.equals(String)
但是现在您可以编辑类型
,删除等于(字符串)
,并且只重新编译类型
。当你运行程序时,你会得到一个“没有这样的方法异常”,因为编译器假定Types
有一个equals(String)
方法。因此,您的程序失败了,尽管它是一个完全合法的Java程序,并且在新的类型中有一个合适的匹配方法equals(Object)
因此,真正的编译器使用静态类型来确定一个方法是否存在,并且在给定的静态参数下是否合法,并且只有在运行时,JVM才能找到实际类型,并调用该方法的适用重写。编译器只知道一个vriable的静态类型
,并使用它检查java中的一些约束
拳头:
引用变量typ是一种类型,不是吗
我认为编译器将变量类型
视为对象
,当然类型
是指向类型
对象的实点,因为每个类都是对象
的子类型,所以它可以分配给变量类型
第二:
编译器没有足够的信息来调用正确的equals()
不知道。编译器只知道程序调用变量typ
的方法equals()
,但不知道typ
是指向Types
实例还是其他类型实例。因此,在编译时,真正调用的方法并不确定
查看以下代码:
Object typ = new Main();
Object o=new Object();
typ.equals("Hi");//can do this as String is a subclass of Object
o.equals("Hi");
请参阅JVM中的指令集:
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//方法java/lang/Object.equals:(Ljava/lang/Object;)Z
typ.equals(“Hi”)
和o.equals(“Hi”)
编译相同的指令集。但将调用哪个方法取决于实际实例的方法引用表
#43 = Methodref #41.#132 // java/lang/Object.equals:(Ljava/lang/Object;)Z
#43
是指向实际方法的方法引用。jvm中的每个实例都有一个方法引用表。如果类重写其继承方法的某个方法,方法引用将更改为覆盖
方法。抱歉,静态拼写错误。引用变量类型
为静态类型对象
和运行时类型类型
。不,编译器只知道typ
是Object
。