好的模式?<X 扩展了异常> ...方法() 抛出 X

一些背景,然后是一些问题。

我最近才发现接口(或类)可能是其方法可能引发的(已检查)异常类型的泛型。例如:

interface GenericRunnable<X extends Exception> {
    void run() throws X;
}

关键是,如果您稍后使用实例化它并调用该方法,编译器知道您需要捕获或将其标记为抛出。更好的是,如果是,你根本不需要处理它。IOExceptionrunIOExceptionXRuntimeException

这是一个使用上述接口的人为示例,但它基本上是一个回调,应该很常见。

public <X extends Exception> void runTwice(GenericRunnable<X> runnable) throws X {
    runnable.run(); runnable.run();
}
...
public myMethod() throws MyException {
    runTwice(myRunnable);
}

我们调用一个泛型实用程序方法(可能在外部库中定义)来运行我们自己的特定方法和特定的已检查异常,并且我们不会丢失有关可能引发哪个特定已检查异常的任何信息。runTwice

另一种选择是简单地同时使用方法和方法。这不会限制接口的任何实现,但检查异常的优势将丢失。或者可能根本没有,也失去了检查异常的优势,并可能迫使实现包装。throws ExceptionRunnable.runrunTwiceRunnablethrows

因为我从未见过,也许我错过了什么。此外,我已经多次看到回调示例用作反对已检查异常的参数,但未被反驳。(这个问题对检查异常的利弊不感兴趣。throws X

抛出 X 通常是一个好主意吗?有哪些优点和缺点?你能举一些例子,要么使用抛出X,要么没有,但应该有?

基本上,我想要一些进一步的见解。您可以对以下示例进行评论。

  • OutputStreamthrows(也许可以扩展IOExceptionByteArrayOutputStreamGenericOutputStream<RuntimeException>)

  • Callable / Future.get

  • 共享资源池borrowObject / makeObject

(自编辑以来,我不是在问这些是否可以/应该在回想起来以不同的方式设计。相反,会比 更好。throws Xthrows Exception


答案 1

我一直使用这种模式,主要用于函数式java。

优点:

  1. 您可以拥有几种高阶模式(如Viser)的风格,即:

    interface ExceptionalVoidVisitor< E extends Exception > {
        void handleA( A a ) throws E;
        void handleB( B b ) throws E;
    }
    interface VoidVisitor extends ExceptionalVoidVisitor< RuntimeException > {}
    interface ExceptionalVisitor< T, E extends Exception > {
        T handleA( A a ) throws E;
        T handleB( B b ) throws E;
    }
    interface Visitor< T > extends ExceptionalVisitor< T, RuntimeException > {}
    
  2. 你的客户端可以为他可能引发的所有异常声明一个基异常类,你最终会得到一个完全通用的库。

缺点:

  1. 正如你所发现的,没有办法处理泛型类型的异常;你必须让它逃脱。
  2. 另一种选择是接受E的生成器,这对客户端来说可能很尴尬。

答案 2