Java8 лямбды и исключения


интересно, может ли кто-нибудь объяснить мне следующую странность. Я использую Java 8 обновление 11.

учитывая, этот метод

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

если я сначала создаю объект функции и передаю его в метод выше, вещи компилируются.

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

}

но, если я встрою функцию как лямбда, компилятор говорит

несообщаемого исключение Х; должен быть пойман или объявленные быть бросил

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

см. Также здесь для выполняемого тестового кода.

3 53

3 ответа:

это похоже на ошибку JDK-8054569, что не влияет на затмение.

я смог сузить его, заменив функцию поставщиком и извлекая 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")));
}

а затем дальше, удалив поставщиков и лямбды в целом:

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 (так что это не отмечено).

Это то, что решило проблему для меня:

вместо того, чтобы писать

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

я писал:

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

добавление явного <BadRequestException> помогает с этими лямбда-крайними случаями (которые довольно раздражают...)

обновление: это в случае, если вы не можете обновить до последней версии JDK, если вы можете, вы должны...

Если вы пытаетесь скомпилировать чужой проект попробуйте обновить до 1.8.0_92