Java8 Lambdas and Exceptions

JavaLambdaJava 8

Java Problem Overview


I wonder if someone could explain the following weirdness to me. I'm using Java 8 update 11.

Given this method

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

If I first construct a function Object, and pass that in to the method above, things compile.

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

}

But, if I inline the function as a lambda, the compiler says

> unreported exception X; must be caught or declared to be thrown

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

Update: Turns out the error message was abbreviated by maven. If compiled directly with javac, the error is:

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

Also see here for runnable test code.

Java Solutions


Solution 1 - Java

This is what solved the problem for me:

instead of writing

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

I wrote:

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

Adding the explicit <BadRequestException> helps with these lambda edge cases (which are quite annoying...)

UPDATE: This is in case you can't update to the latest JDK version, if you can you should...

Solution 2 - Java

This looks like a case of bug JDK-8054569, which doesn't affect Eclipse.

I was able to narrow it down by replacing Function with Supplier and extracting the orElseThrow method:

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

and then further by removing the suppliers and lambdas altogether:

abstract <T> void f(T t);

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

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

which is actually a cleaner example than the one in the bug report. This shows the same error if compiled as Java 8, but works fine with -source 1.7.

I guess something about passing a generic method return type to a generic method parameter causes the type inference for the exception to fail, so it assumes the type is Throwable and complains that this checked exception type isn't handled. The error disappears if you declare bug() throws Throwable or change the bound to X extends RuntimeException (so it's unchecked).

Solution 3 - Java

If you are trying to compile someone else's project try to upgrade to 1.8.0_92

Solution 4 - Java

Similar to @keisar I could solve my problem (see https://stackoverflow.com/questions/54140830/maven-compiler-plugin-fails-to-compile-a-file-that-eclipse-has-no-problem-with) by specifying the type parameter.

However, I found it much more convenient (since I use NotFoundException in a number of places) to simply make my exception class its own Supplier:

public class NotFoundException extends RuntimeException
    implements Supplier<NotFoundException> {

	// Rest of the code

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

}

Then you can simply do:

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

Solution 5 - Java

If explicitly adding <RunTimeException> looks ugly then as a workaround you can replace with orElseGet()

.orElseGet(() -> throw new RunTimeException("foo"));

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionrompetrollView Question on Stackoverflow
Solution 1 - JavakeisarView Answer on Stackoverflow
Solution 2 - JavaSean Van GorderView Answer on Stackoverflow
Solution 3 - JavaIgor KatkovView Answer on Stackoverflow
Solution 4 - JavamarkvgtiView Answer on Stackoverflow
Solution 5 - JavaPriyanshu TomarView Answer on Stackoverflow