为什么在使用超类引用调用子类方法时会出现编译时错误?

我知道在方法覆盖的情况下会发生多态。 但我对下面的内容感到有些困惑。

class A { public void hi() { System.out.println("A "+this.getClass().getName()); } } class B extends A { public void bye() { System.out.println("B "+this.getClass().getName()); } } class Ideone { public static void main (String[] args) throws java.lang.Exception { A a = new B(); a.hi(); a.bye(); } } 

输出:

 Main.java:35: error: cannot find symbol a.bye(); ^ symbol: method bye() location: variable a of type A 1 error 

为什么这会产生编译时错误?

a = new B()B类对象是在运行时创建的,因此a一个指向B类对象的引用变量。

现在,如果我们调用B的类方法bye() ,为什么它是编译器时间错误?

a变量可以在运行时包含A类的实例或A任何子类。 因此,您只能为该变量调用AA方法。

编译器在确定哪些方法调用有效时,只关心变量的编译时类型。

变量a的声明类型是A.编译器不知道(也不应该知道)它在运行时的具体类型是什么B.它只知道它是A,并且没有bye()方法在一个。

整个做点

 A a = new B(); 

并不是

 B a = new B(); 

是清楚地说a是A,并且可以有任何具体类型,只要conrete类型扩展A.如果你以后找到更好的A实现,你必须能够将该行更改为

A =新的BetterImplementation();

并编译代码。

将新的B实例分配给类型A的引用就像让它佩戴唯一看到方法定义的A眼镜。 那就是你得到A所描述的界面。 即使该实例具有bye的实现,您也只能访问hi

理解这一点的一个简单方法是A a实际上可能指向B的对象,但在编译期间编译器不知道这一点。

编译器只检查引用即’a’并查看它的类,即’A’是否具有其引用’a’试图调用的方法。

在您的情况下,引用’a’没有一个名为bye()的方法,因此根据编译器不能进行这样的调用,这就是您看到该错误的原因。

我想你已经从其他答案得到了你的多态性问题的答案。 除此之外,您还可以使用类型转换以这种方式使代码工作。

 (B)a.bye(); 

这是一个很好的解释,您可以阅读有关引用与对象类型的内容。 这是关于铸造的好文章。

您只能从父类类型访问父类方法。

 A a = new B(); 

解决方案1:

将父级更改为子级( B b = new B () )并调用B方法。

解决方案2:

检查parent是否是Child对象的实例。 如果是,则将父项转换为Child并调用该方法。

 class Ideone { public static void main (String[] args) throws java.lang.Exception { A a = new B(); a.hi(); if ( a instanceof B){ ((B)a).bye(); } } } 

输出:

 AB BB 

看一下instanceof上的oracle文档页面

instanceof运算符将对象与指定的类型进行比较。 您可以使用它来测试对象是否是类的实例,子类的实例或实现特定接口的类的实例。

多态性基本上是一个对象采取多种forms的能力。 由于您的父类是指子类对象,因此它是多态的。

 Main.java:35: error: cannot find symbol a.bye(); ^ symbol: method bye() location: variable a of type A 1 error 

您收到此错误是因为您尝试从超类引用调用子类的方法。

在这里,您将subClass对象ref分配给超类,这很好。 但是超类只能调用它自己已知的方法和数据。

 A a = new B(); a.hi(); // member of super class a.bye(); // Not a member a super class