在Kathey Sierra的SCJP书中,摘录如下:
如果方法被覆盖但是你使用了多态(超类型) 引用用重写方法引用子类型对象, 编译器假设你正在调用的超类型版本 方法。如果超类型版本声明了一个已检查的异常,但是 重写子类型方法没有,编译器仍然认为你是 调用声明异常的方法(更多内容见第5章)。
我们来看一个例子:
class Animal { public void eat() throws Exception { // throws an Exception } } class Dog2 extends Animal { public void eat() { /* no Exceptions */ } public static void main(String[] args) { Animal a = new Dog2(); Dog2 d = new Dog2(); d.eat(); // ok a.eat(); // compiler error - // unreported exception } }
由于在Animal上声明的异常,此代码将无法编译 吃()方法。即使在运行时,eat()也会发生这种情况 使用的方法是Dog版本,它没有声明 异常。
现在我不明白的是,a.eat();
如何引发编译错误?
(即使在Super的情况下,子项中的重写函数也可能没有任何异常)
答案 0 :(得分:1)
编译器不知道所引用对象的实际类型。它仅检查分配是否有效。
对a.eat
的调用导致编译错误,因为编译器不知道a引用的Animal是Dog2。它决定是否可以仅根据引用Dog2的变量的类型抛出已检查的异常。
编译器不聪明。它不运行代码,也不跟踪分配给变量a
的对象的实际类。
就像你引用具有超类类型的对象一样,你不会看到特定于子类的方法,你也会看到超类的throws-clause,而不是子类的throws-clause。方法
如果在最后一行中向子类添加强制转换:
((Dog2)a).eat();
然后你不会得到未报告的异常编译错误。
答案 1 :(得分:1)
当您查看代码时,您会意识到:当您调用eat()的对象是
因此,第二种用法导致编译器向您抱怨。
重写方法并减少引发签名非常好。当子类从不抛出异常时,知道如何处理异常的调用者肯定可以工作。