SCJP - 具有异常处理的覆盖方法引发编译器错误

时间:2016-12-04 02:38:11

标签: java exception override scjp

在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的情况下,子项中的重写函数也可能没有任何异常)

2 个答案:

答案 0 :(得分:1)

编译器不知道所引用对象的实际类型。它仅检查分配是否有效。

a.eat的调用导致编译错误,因为编译器不知道a引用的Animal是Dog2。它决定是否可以仅根据引用Dog2的变量的类型抛出已检查的异常。

编译器不聪明。它不运行代码,也不跟踪分配给变量a的对象的实际类。

就像你引用具有超类类型的对象一样,你不会看到特定于子类的方法,你也会看到超类的throws-clause,而不是子类的throws-clause。方法

如果在最后一行中向子类添加强制转换:

((Dog2)a).eat();

然后你不会得到未报告的异常编译错误。

答案 1 :(得分:1)

当您查看代码时,您会意识到:当您调用eat()的对象是

  • 狗,则通话不会引发异常
  • 一个动物,那么那个叫声可能会抛出

因此,第二种用法导致编译器向您抱怨。

重写方法并减少引发签名非常好。当子类从不抛出异常时,知道如何处理异常的调用者肯定可以工作。