使用 Java 构造函数抛出异常的类的子类化

Subclassing from classses with Java Constructors throwing Exceptions

我的理解是,subclass 中的重写方法不应该抛出异常或与父 class 的基本方法相比抛出更窄的异常。为什么它在构造函数中工作相反,subclass 的构造函数必须抛出相同或更广泛的异常,对此有什么合理的解释吗?

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
}

Why does it work the opposite in constructors, the constructor of subclass has to throw the same exception or wider, Any reasonable explanation for this?

子类构造函数总是通过调用 super(..) 来调用其父构造函数。在这种情况下,父构造函数被声明为抛出 MyException 类型的已检查异常。您的子类构造函数必须能够处理该问题(使用 throws 因为 super(..) 必须是构造函数主体中的第一条语句)。

使用方法,您不必调用 super 实现。

方法的声明异常是其 public 契约的一部分。声明的异常是方法可能抛出的异常,但并不是必须方法抛出的异常。覆盖方法不需要与它们覆盖的方法具有完全相同的签名;他们可以有一个限制较少的签名。

考虑以下示例:

class A { 
    A f() throws MyException { ... }
}
class B {
    @Override
    B f() throws MySubException { ... }
}
class C {
    void g(A a) {
        ...
    }
}

此处,class B 的方法 f 覆盖 class A 的方法 f 尽管它没有确切的相同的签名。但是 B 的所有实例都满足 A 的约定,因为方法 B.f 实际上是 returns 一个 A 实例并且不能抛出检查异常,除非它是MyException 的子class。因此,我们可以安全地将 B 实例传递给任何需要 A 引用的方法,例如 C class.[=25 中的 g(A a) =]

对于构造函数,情况并非如此。首先,构造函数不属于实例,它们属于class,并且构造函数永远不会覆盖另一个构造函数。但它们总是(隐式或显式)调用 superclass 的构造函数。当此构造函数声明已检查的异常时,构造函数必须使用 try-catch 块处理它们,或者自己声明它们。

注意@Hoopje,当用super()调用超类的构造函数时,你不能在try{}catch中捕获异常,你也必须在子类构造函数中声明throws。