我的理解是,子类中的重写方法不应该抛出异常,也不应该抛出比父类的基方法更窄的异常。为什么它在构造函数中起相反的作用,子类的构造函数必须抛出相同的异常或更广泛的异常,对此有任何合理的解释吗?
class MyException extends Exception{}
class MySubException extends MyException{}
class MySubSubException extends MySubException{}
public class Alpha {
public Alpha() throws MyException{
}
void foo() throws MyException {}
}
class Beta extends Alpha{
public Beta() throws MyException{ //NOT MySubSubException
super();
}
void foo() throws MySubException {} //Ok for methods
}
为什么它在构造函数中起相反的作用子类必须抛出相同或更宽的异常,任何合理的对此有何解释?
子类构造函数总是通过调用super(..)
来调用其父构造函数。在这种情况下,父构造函数被声明为抛出类型为MyException
的已检查异常。您的子类构造函数必须能够处理此问题(使用throws
,因为super(..)
必须是构造函数主体中的第一个语句)。
使用方法,您不必强制调用super
实现。
方法的声明异常是其公共约定的一部分。声明的异常是可以由方法抛出的异常,但不是必须由方法抛出它们。重写方法不需要与它们重写的方法具有完全相同的签名;他们可以有一个限制较少的签名。
考虑以下示例:
class A {
A f() throws MyException { ... }
}
class B {
@Override
B f() throws MySubException { ... }
}
class C {
void g(A a) {
...
}
}
这里,类B
的方法f
覆盖类A
的方法f
,尽管它不具有完全相同的签名。但是B
的所有实例都满足了A
的约定,因为方法B.f
确实返回了一个A
实例,并且不能抛出已检查的异常,除非它是MyException
的子类。因此,我们可以安全地将B
实例传递给任何要求A
引用的方法,例如C
类中的g(A a)
。
对于构造函数来说,情况并非如此。首先,构造函数不属于实例,它们属于class并且构造函数从不覆盖另一个构造函数。但它们总是(隐式或显式)调用超类的构造函数。当此构造函数声明检查异常时,构造函数必须使用try-catch
块处理它们,或者自己声明它们。
注意@Hoopje,当用super()调用超类的构造函数时,您无法在try{}catch中捕获异常,您还必须在子类构造函数中声明throws。