【问题标题】:Java 8 - throw multiple generic checked exceptions in lambdaJava 8 - 在 lambda 中抛出多个通用检查异常
【发布时间】:2019-03-04 14:54:59
【问题描述】:

在我正在从事的一个项目中,我发现了一个类,它在一些复杂的异常处理中包装了它的超类的所有方法。它看起来类似于:

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>

我立刻想到“哇,怎么会有一个通用包装器并在每个方法中调用它。这个类会短 10 倍!”。 所以我得工作了。

这就是我卡住的地方:

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。

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

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

【问题讨论】:

  • 我想说的问题之一是您的 wrapMethod() 需要在其 throws 子句中支持多个异常,以便像在 method2() 中那样调用它并抛出 @987654328 @ 和/或 ExceptionC。因此,您可能需要为多个异常提供重载(可能不会那么多,因为 IMO 的 throws 子句中有 20 个异常的方法无论如何都是不好的设计)。由于您需要多个 wrapMethod(),因此您可能需要将转换和日志记录部分提取到单独的方法中 - 如果可能的话。
  • @Thomas 我尝试了 5 个通用异常而不是 1 个的方法。它没有改变任何事情。包装 method1 仍然有效,包装 method2 仍然默认为 Exception。所以它不会起作用,除非你建议我应该为每个可能的异常数量创建一个单独的包装器,但这会破坏这个练习的要点,即使代码更短。
  • 你应该清理语法。这是interface ThrowingMethod&lt;E extends Exception&gt;,而不是interface &lt;E extends Exception&gt; ThrowingMethod。此外,无需将接口声明为static。这总是暗示的。
  • 好吧,如果你所有的方法都有不同数量的异常抛出,那么是的,这个练习没有实际意义。如果不是,那么它可能仍然值得,特别是因为接口声明不是那么大,并且可以提取到单独的文件中(并且在其他情况下也可以重用)。
  • @M.Prokhorov 和javac没有区别

标签: java lambda java-8 exception-handling


【解决方案1】:

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

【讨论】:

  • 我只是尝试过(后半部分),似乎没有解决问题:Eclipse 的编译器错误仍然相同。
  • 我需要了解更多关于 AOP 的知识来决定我是否可以使用它。这比示例显示的要复杂一些。 wrapMethod 接受额外的参数。我现在正在寻找我最初尝试的解决方案。
【解决方案2】:

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

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);
}

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

【讨论】:

    猜你喜欢
    • 2018-10-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-09-21
    相关资源
    最近更新 更多