Java 8 - 在 lambda 中抛出多个通用检查异常

Java 8 - throw multiple generic checked exceptions in lambda

在我工作的一个项目中,我发现了一个 class ,它在一些复杂的异常处理中包装了它的 super-class 的所有方法。它看起来类似于:

public void method1() throws ExceptionA {
    String exceptionString = "";
    try {
        super.method1();
    } catch (ExceptionA e) {
         exceptionString = // <convert the exception to string in an elaborate way>
         throw e;
    } finally {
         // <an elaborate logger call which uses value of exceptionString>
    }
}

public void method2() throws ExceptionB, ExceptionC {
    String exceptionString = "";
    try {
        super.method2();
    } catch (ExceptionB | ExceptionC e) {
         exceptionString = // <convert the exception to string in elaborate way>
         throw e;
    } finally {
         // <an elaborate logger call which uses value of exceptionString>
    }
}

// ... <a bunch of other methods like this>

我立马"Wow, how could would it be to have one generic wrapper and just call it in every of these methods. The class would be like 10x shorter!"。 所以我得工作了。

这是我卡住的地方:

private interface ThrowingMethod<E extends Exception> {
    void run() throws E;
}

public <E extends Exception> void wrapMethod(ThrowingMethod<E> method) throws E {
    String exceptionString = "";
    try {
        method.run();
    } catch (Exception e) {
         exceptionString = // <convert the exception to string in an elaborate way>
         throw e;
    } finally {
         // <an elaborate logger call which uses value of exceptionString>
    }
}

public void method1() throws ExceptionA {
    wrapMethod(super::method1); // works
}

public void method2() throws ExceptionB, ExceptionC {
    wrapMethod(super::method2); // Error in Eclipse: "Unhandled exception type Exception"
}

// ... <a bunch of other methods like this>

总之,这种方法适用于只抛出一种检查异常的方法。当方法抛出多个已检查的异常时,Java 假定异常类型为 Exception.

我尝试向 ThrowingMethodwrapMethod 添加更多通用参数,但它没有任何改变。

如何获得一个功能接口来处理多个通用异常?

所以你的目标只是用日志包装一堆方法?处理此问题的典型方法是使用 AOP。您只需创建一个匹配所有这些方法的切入点,就不会有一堆重复的样板文件。不需要那些接口或包装方法。

当您扩展界面以使用两个类型变量时,即

private static interface ThrowingMethod<E1 extends Exception,E2 extends Exception> {
    void run() throws E1, E2;
}

public <E1 extends Exception,E2 extends Exception>
void wrapMethod(ThrowingMethod<E1,E2> method) throws E1,E2 {
    // same as before
}

关于类型推断的规则没有改变,它们对于两个类型变量都是相同的。例如。你仍然可以使用

public void method1() throws ExceptionA {
    wrapMethod(super::method1);
}

和以前一样,因为编译器只是为两个类型变量推断出相同的单一异常类型。

对于声明两个异常的方法,它不会为第一个类型变量选择一个而为第二个类型变量选择另一个;没有规则可以告诉编译器将哪个异常用于哪个类型变量。

但是在这种情况下你可以帮助编译器,例如

public void method2() throws ExceptionB, ExceptionC {
    wrapMethod((ThrowingMethod<ExceptionB, ExceptionC>)super::method2);
}

这是您可以通过这种方法获得的最佳效果。