I have a problem trying out the Lambda expressions of Java 8.
Usually it works fine, but now I have methods that throw IOException
's.
It's best if you look at the following code:
class Bank{
....
public Set<String> getActiveAccountNumbers() throws IOException {
Stream<Account> s = accounts.values().stream();
s = s.filter(a -> a.isActive());
Stream<String> ss = s.map(a -> a.getNumber());
return ss.collect(Collectors.toSet());
}
....
}
interface Account{
....
boolean isActive() throws IOException;
String getNumber() throws IOException;
....
}
The problem is, it doesn't compile, because I have to catch the possible exceptions of the isActive- and the getNumber-Methods. But even if I explicitly use a try-catch-Block like below, it still doesn't compile because I don't catch the Exception. So either there is a bug in JDK, or I don't know how to catch these Exceptions.
class Bank{
....
//Doesn't compile either
public Set<String> getActiveAccountNumbers() throws IOException {
try{
Stream<Account> s = accounts.values().stream();
s = s.filter(a -> a.isActive());
Stream<String> ss = s.map(a -> a.getNumber());
return ss.collect(Collectors.toSet());
}catch(IOException ex){
}
}
....
}
How can I get it work? Can someone hint me to the right solution?
You can also propagate your static pain with lambdas, so the whole thing looks readable:
propagate
here receivesjava.util.concurrent.Callable
as a parameter and converts any exception caught during the call intoRuntimeException
. There is a similar conversion method Throwables#propagate(Throwable) in Guava.This method seems being essential for lambda method chaining, so I hope one day it will be added to one of the popular libs or this propagating behavior would be by default.
If you don't mind using 3rd party libraries, AOL's cyclops-react lib, disclosure::I am a contributor, has a ExceptionSoftener class that can help here.
To properly add IOException (to RuntimeException) handling code, your method will look like this:
The problem now is that the
IOException
will have to be captured as aRuntimeException
and converted back to anIOException
-- and that will add even more code to the above method.Why use
Stream
when it can be done just like this -- and the method throwsIOException
so no extra code is needed for that too:Your example can be written as:
The Unthrow class can be taken here https://github.com/SeregaLBN/StreamUnthrower
The functional interfaces in Java don’t declare any checked or unchecked exception. We need to change the signature of the methods from:
To:
Or handle it with try-catch block:
Another option is to write a custom wrapper or use a library like ThrowingFunction. With the library we only need to add the dependency to our pom.xml:
And use the specific classes like ThrowingFunction, ThrowingConsumer, ThrowingPredicate, ThrowingRunnable, ThrowingSupplier.
At the end the code looks like this:
You must catch the exception before it escapes the lambda:
Consider the fact that the lambda isn't evaluated at the place you write it, but at some completely unrelated place, within a JDK class. So that would be the point where that checked exception would be thrown, and at that place it isn't declared.
You can deal with it by using a wrapper of your lambda that translates checked exceptions to unchecked ones:
Your example would be written as
In my projects I deal with this issue without wrapping; instead I use a method which effectively defuses compiler's checking of exceptions. Needless to say, this should be handled with care and everybody on the project must be aware that a checked exception may appear where it is not declared. This is the plumbing code:
and you can expect to get an
IOException
thrown in your face, even thoughcollect
does not declare it. In most, but not all real-life cases you would want to just rethrow the exception, anyway, and handle it as a generic failure. In all those cases, nothing is lost in clarity or correctness. Just beware of those other cases, where you would actually want to react to the exception on the spot. The developer will not be made aware by the compiler that there is anIOException
to catch there and the compiler will in fact complain if you try to catch it because we have fooled it into believing that no such exception can be thrown.