为什么我不能捕获抛出泛型的调用的已检查异常?

Gho*_*ica 13 java generics exception-handling exception try-catch

我正在研究一个应该调用任意代码的小助手(以lambda形式传入).帮助器应该捕获某些异常,并将它们放在一些包装器中.我的"自己的"例外不应该被包裹,而是重新抛出.

我想出了这段代码:

@FunctionalInterface
interface Processable<T, X extends Throwable> {
    public T apply() throws X;
}

class MyCheckedException extends Exception { ... }
class MyCheckedExceptionWrapper extends MyCheckedException { ... }

public class MyExceptionLogger<T, X extends Throwable> {
    public T process(Processable<T, X> processable) throws MyCheckedException {
        try {
            return processable.apply();
        } catch (MyCheckedException thrown) { // this line isn't accepted
            throw thrown;
        } catch (Exception | LinkageError thrown) {
            throw new MyCheckedExceptionWrapper(thrown);
        } catch (Throwable thrown) {
           ... just log
          return null; 
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

以上给出了编译错误:

MyCheckedException的无法访问的catch块.永远不会从try语句体MyExceptionLogger抛出此异常...

换句话说:虽然apply()定义为抛出一些X extends Throwable但是在调用该方法时我无法捕获特定的已检查异常.

知道我可以通过捕获Throwable来使用代码,然后使用instanceof检查 - 但我想了解为什么不可能有如上所述的try/catch.

das*_*ght 6

此行为在JLS的第11.2.3节中指定:

这是,如果一个catch子句可以捕获检查异常类编译时错误E1,这是不是对应的catch子句的try块可以抛出checked异常类,它是一个子类或超类的情况下E1,除非E1Exception或超的Exception.

MyCheckedException适合E1上面类的描述,因为它没有在泛型声明中声明processable.apply(),并且它不是Exception或它的超类之一.编译器只知道该方法可以抛出一个Throwable,因此MyCheckedException不会声明.


dpr*_*dpr 2

我认为 - 基于 JSL - 没有充分的理由说明为什么您不应该能够在示例中捕获自定义检查异常。

如果您阅读 JLS 的引用

如果 catch 子句可以捕获检查异常类,则为编译时错误,E1但与 catch 子句对应的 try 块不能抛出作为 的子类或超类的检查异常类E1,除非E1Exception或超类的Exception

如果相应的 try 块中的方法声明了 ,则应允许 catch 子句捕获任何已检查的。在您的示例中,try 块可以抛出一个检查异常类,该异常类是 的子类或超类但显然不是 的子类或超类,或者是 的超类ExceptionThrowableMyCheckedExceptionThrowableMyCheckedExceptionExceptionException

这可以通过从上面的示例中删除泛型来轻松验证,并查看它的编译没有问题:

@FunctionalInterface
interface Processable<T> {
    public T apply() throws Throwable;
}


private <T> T process(Processable<T> aProcessable) {
    try {
        return aProcessable.apply();
    } catch (MyCheckedException e) {
        e.printStackTrace();
    } catch (Exception e) {
        e.printStackTrace();
    } catch (Throwable e) {
        e.printStackTrace();
    }

    return null;
}
Run Code Online (Sandbox Code Playgroud)

也就是说,这个问题在某种程度上必须与泛型与异常的结合使用有关。也许这与类型擦除有关,但是对于擦除类型,您的示例也可以正常工作:

@FunctionalInterface
interface Processable {
    public Object apply() throws Throwable;
}


private Object process(Processable aProcessable) {
    try {
        return aProcessable.apply();
    } catch (MyCheckedException e) {
        e.printStackTrace();
    } catch (Exception e) {
        e.printStackTrace();
    } catch (Throwable e) {
        e.printStackTrace();
    }

    return null;
}
Run Code Online (Sandbox Code Playgroud)