目前尚不清楚您的问题实际上是基于什么。
当你有表格的代码时
Dog d = new Dog();
d.eat();
d
is的静态类型,Dog
因此,编译器将Dog.eat()
在检查调用是否正确后将调用编码到类文件中。
对于调用,有几种可能的情况
Dog
可能会声明一个方法eat()
,该方法会覆盖在其超类中具有相同签名的方法Animal
,就像在您的示例中一样
Dog
可能声明一个eat()
不覆盖另一个方法的方法
Dog
可能不声明匹配方法,但从其超类或实现的接口继承匹配方法
请注意,它完全不相关,适用于哪种情况。如果调用有效,它将被编译为 的调用Dog.eat()
,无论应用哪种情况,因为d
在其eat()
上调用的正式静态类型是Dog
。
与实际场景无关也意味着在运行时,您可能有不同版本的 class Dog
,适用于另一个场景,而不会破坏兼容性。
如果你写了这将是一个不同的画面
Animal a = new Dog();
a.eat();
a
现在is的正式类型Animal
和编译器将检查是否Animal
包含 的声明eat()
,无论它是否被覆盖Dog
。然后,此调用将被编码为Animal.eat()
字节码中的目标,即使编译器可以推断出它a
实际上是对Dog
实例的引用。编译器只是遵循正式规则。Animal
这意味着如果运行时版本缺少eat()
方法,即使有方法,此代码也将不起作用Dog
。
这意味着删除基类中的方法将是一个危险的更改,但您始终可以重构代码,添加更抽象的基类并将方法向上移动到类层次结构中,而不会影响与现有代码的兼容性。这是 Java 设计者的目标之一。
因此,也许,您编译了上面两个示例之一,稍后,您正在使用较新的库版本运行代码,其中类型层次结构是Animal
>>Carnivore
并且没有实现Dog
,因为最具体的自然位置实施是. 在那种环境中,您的旧代码仍将运行并做正确的事情,没有问题。Dog
eat()
Carnivore.eat()
进一步注意,即使您重新编译旧代码而不做任何更改,但使用较新的库,它将与旧库版本保持兼容,因为在您的代码中,您永远不会引用新Carnivore
类型,编译器将使用正式类型,你在你的代码中使用,Animal
或者,根据上面解释的正式规则,没有记录从编译代码中继承方法Dog
的事实。这里没有惊喜。Dog
eat()
Carnivore