超类引用无法在 Java 中调用子类方法

Superclass reference not able to call subclass method in Java

我对Java中的多态性有一个基本的怀疑。我将下面的代码写在一个名为 AnimalTestDrive.java 的文件中。根据我的说法,下面的代码应该特别适用于粗体行,但不幸的是它不是。你能解释一下为什么吗,我给出了以下错误:

class Dog extends Animal {

    public void dogMethod() {
        System.out.println("In Dog method");
    }
}

public class AnimalTestDrive {
    public static void main(String args[]) {
        Dog d = new Dog();
        d.dogMethod();
        d.animalMethod();

        Animal animal = new Animal();
        animal.animalMethod();

        animal = d;
        **animal.dogMethod(); // THIS IS NOT WORKING**

    }
}

据Java所知,动物就是动物,所以它只能做你的动物class中定义的事情。如果您希望能够使用 Dog 方法,并且您知道您的动物是 Dog,则必须将其转换为 Dog 才能使该方法可见。

换句话说,变量唯一可用的方法和字段是由其左侧类型定义的方法和字段。你可以说 ((Dog)animal).dogMethod();将动物称为狗,或创建一个新变量 Dog animalAsDog = animal; 并在 animalAsDog 上调用您的方法。

让我们尝试以与编译器相同的方式查看这一行:

animal.dogMethod();

首先,它需要弄清楚animal是什么意思。这很好也很简单 - 它是当前方法中的一个局部变量,所以不需要看得太远。

该变量的编译时类型是 Animal。编译器不关心变量的 value 在执行时是什么——它只使用关于声明类型的信息。

所以,这就是它用来尝试在 animal 的上下文中查找 dogMethod() 的含义,即类型 Animal。首先它在 Animal 中查找,然后在 java.lang.Object 中查找(Animal 的隐式 superclass)——但是这些 classes 都不包含 [= 的声明20=]。那时,编译器不得不报错放弃——它找不到方法。该方法在 execution-time 类型的对象(animal 所指的值)上可用并不重要。它必须在编译时绑定它,仅使用编译时可用的信息。

执行 时做出的唯一决定是使用哪个方法的实现 - 例如,如果您调用 animal.toString() 并且 Dog class 有一个覆盖,例如

@Override public String toString() {
    return "I'm a dog";
}

然后编译器将从 java.lang.Object 中找到 toString() 方法,因此它会知道该方法调用是有效的 - 但 实现 [由于对象的执行时类型,将使用 =23=]。