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

10
在我正在处理的一个项目中,我发现了一个类,它用一些复杂的异常处理包装了其父类的所有方法。 它看起来类似于这样:
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>

我当即想到:“哇,如果有一个通用的包装器,在每个方法中只需调用它,那将会多么方便。这个类会短小十倍!”

于是我开始动手。

这就是我卡住的地方:

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 添加更多的泛型参数,但它并没有改变任何东西。

我如何才能让一个函数式接口处理多个泛型异常?


@Thomas 我尝试了使用5个通用异常的方法,而不是1个。但这并没有改变任何事情。包装method1仍然有效,而包装method2仍然默认为Exception。所以这不起作用,除非你建议我为每个可能的异常数量制作单独的包装器,但这将违背这个练习的目的,即使代码更短。 - Piotr Siupa
1
你应该清理一下语法。它应该是 interface ThrowingMethod<E extends Exception>,而不是 interface <E extends Exception> ThrowingMethod。此外,没有必要将接口声明为 static。这总是被隐含的。 - Holger
如果你的所有方法抛出的异常数量都不同,那么这个练习就没有意义了。如果不是这样,那么它仍然可能是值得的,特别是因为接口声明并不是很大,可以提取到单独的文件中(并在其他情况下重复使用)。 - Thomas
这只是Eclipse编译器的缺陷吗?你尝试过使用javac编译吗? - M. Prokhorov
1
@M.Prokhorov,使用javac没有任何区别。 - Holger
显示剩余2条评论
2个回答

5
当您扩展界面以使用两个类型变量时,即:
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);
}

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

0

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


我刚刚尝试了那个(后半部分),似乎并没有解决问题:Eclipse仍然报同样的编译器错误。 - M. Prokhorov
我需要学习更多关于AOP的知识来决定是否可以使用它。这比示例展示的要稍微复杂一些。'wrapMethod' 需要额外的参数。目前我正在寻找解决我原始尝试的方案。 - Piotr Siupa

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接