13

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

我最近才发现接口(或类)在其方法可能抛出的(已检查)异常类型中可能是通用的。例如:

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

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

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

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

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

另一种方法是简单地使用throws Exception方法Runnable.runrunTwice方法。这不会限制Runnable接口的任何实现,但会失去检查异常的优势。或者可能根本没有throws,也失去了检查异常的优势并可能强制实现包装。

因为我从未见过throws X,也许我错过了一些东西。此外,我已经多次看到回调示例用作反对检查异常的参数而没有被反驳。(这个问题对检查异常的优缺点不感兴趣。)

一般是throws X个好主意吗?优缺点都有什么?你能举一些使用throws X或没有但应该有的例子吗?

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

  • OutputStream抛出IOException(也许ByteArrayOutputStream可以延伸GenericOutputStream<RuntimeException>

  • Callable/Future.get

  • 公共池borrowObject/makeObject

(自编辑以来,我不是在问这些是否可以/应该在回想起来时进行不同的设计。相反,会throws Xthrows Exception。)

4

1 回答 1

3

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

优点:

  1. 您可以使用多种高阶模式,例如 Visitor,即:

    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 的生成器,这对客户来说可能很尴尬。
于 2014-02-09T05:25:57.173 回答