Java8 Lambdas and Exceptions

2022-08-31 12:59:51

我想知道是否有人可以向我解释以下怪异之处。我使用的是 Java 8 update 11。

给定此方法

private <F,T> T runFun(Function<Optional<F>, T> fun, Optional<F> opt) {
   return fun.apply(opt) ;
}

如果我首先构造一个函数 Object,并将其传递给上面的方法,那么事情就会编译。

private void doesCompile() {
    Function<Optional<String>, String> fun = o -> o.orElseThrow(() -> new RuntimeException("nah"));
    runFun(fun, Optional.of("foo"));

}

但是,如果我将函数作为lambda内联,编译器会说

未报告的异常 X;必须被捕获或声明被抛出

private void doesNotCompile () {
    runFun(o -> o.orElseThrow(() -> new RuntimeException("nah")), Optional.of("foo"));
}

更新:事实证明,错误消息是由maven缩写的。如果直接使用javac编译,则错误为:

error: unreported exception X; must be caught or declared to be thrown
            runFun(o -> o.orElseThrow(() -> new RuntimeException("nah")), Optional.of("foo"));
                                     ^
  where X,T are type-variables:
    X extends Throwable declared in method <X>orElseThrow(Supplier<? extends X>)
    T extends Object declared in class Optional

有关可运行的测试代码,另请参阅此处


答案 1

这就是为我解决问题的原因:

而不是写作

optional.map(this::mappingFunction).orElseThrow(() -> new BadRequestException("bla bla"));

我写道:

optional.map(this::mappingFunction).<BadRequestException>orElseThrow(() -> new BadRequestException("bla bla"));

添加显式有助于这些 lambda 边缘情况(这很烦人...)<BadRequestException>

更新:这是以防万一你不能更新到最新的JDK版本,如果可以的话,你应该...


答案 2

这看起来像是错误JDK-8054569的情况,这不会影响Eclipse。

我能够通过将函数替换为 Supplier 并提取方法来缩小范围:orElseThrow

abstract <T> void f(Supplier<T> s);

abstract <T, X extends Throwable> T g(Supplier<? extends X> x) throws X;

void bug() {
    f(() -> g(() -> new RuntimeException("foo")));
}

然后进一步完全删除供应商和lambdas:

abstract <T> void f(T t);

abstract <T, X extends Throwable> T g(X x) throws X;

void bug() {
    f(g(new RuntimeException("foo")));
}

这实际上是一个比错误报告中的例子更清晰的例子。如果编译为 Java 8,则显示相同的错误,但与 .-source 1.7

我猜想将泛型方法返回类型传递给泛型方法参数会导致异常的类型推断失败,因此它假设该类型是Spredable并抱怨此检查的异常类型未得到处理。如果声明或更改绑定到(因此未选中),则错误将消失。bug() throws ThrowableX extends RuntimeException


推荐