Java8 Lambda和异常


73

我想知道是否有人可以向我解释以下怪异现象。我正在使用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

另请参阅此处以获取可运行的测试代码。


6
听起来像是另一个类型推断错误。由于Eclipse使用了自己的编译器,因此请确保包括从中得到此错误的编译器的详细信息。
Marko Topolnik 2014年

请注意,如果您使用匿名类,也会发生相同的事情,因此这不是lambda问题。
user2504380 2014年

7
顺便说一句:Eclipse可以很好地进行编译,使用o.<RuntimeException>orElseThrow(()...可以解决问题,但是,这没有必要。(我认为前一段时间在另一个stackoverflow问题中报告了类似的问题,但我现在没有找到它……)
Marco13,2014年

“ mvn -e”可能会有所帮助。
托尔比约恩Ravn的安徒生

Answers:


113

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

而不是写作

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

我写:

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

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

更新:如果您不能更新到最新的JDK版本,则应...


3
我使用的是最新版本的Java,但仍然出现错误。<RuntimeException的> getOrElseThrow(抛出帮了我,谢谢。
汉斯·沃斯特

这对我有所帮助,因为我不拥有该机器而无法升级
Alex

3
我在1.8.0_144上,仍然必须使用您的解决方案。它帮助
olyv

1
Oracle的1.8.0_171也已损坏。
亚伦·迪古拉

2
1.8.0_181还需要显式强制转换。
斯文,

46

这看起来像是一个错误JDK-8054569,它不会影响Eclipse。

我可以通过将功能替换为供应商并提取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")));
}

然后进一步删除所有供应商和lambda:

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

我猜想有关将泛型方法返回类型传递给泛型方法参数的事情会导致异常的类型推断失败,因此它假定该类型为Throwable,并且抱怨此检查的异常类型未处理。如果您声明bug() throws Throwable或将绑定更改为X extends RuntimeException(因此未选中),错误将消失。


11
从1.8.0_45更新到1.8.0_92,它解决了我的问题。谢谢
arnm


1

与@keisar类似,我可以解决我的问题(请参阅maven-compiler-plugin无法编译Eclipse没问题的文件通过指定type参数)。

但是,我发现将NotFoundException异常类设为自己的异常类更方便(因为我在很多地方都使用过)Supplier

public class NotFoundException extends RuntimeException
    implements Supplier<NotFoundException> {

    // Rest of the code

    @Override
    public NotFoundException get() {
        return this;
    }

}

然后,您可以简单地执行以下操作:

// Distribution.rep().get(id) returns a java.util.Optional
Distribution distro = Distribution.rep().get(id).orElseThrow(
    new NotUniqueException("Exception message"));

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.