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?
Keeping this issue in mind I developed a small library for dealing with checked exceptions and lambdas. Custom adapters allow you to integrate with existing functional types:
https://github.com/TouK/ThrowingFunction/
This
UtilException
helper class lets you use any checked exceptions in Java streams, like this:Note
Class::forName
throwsClassNotFoundException
, which is checked. The stream itself also throwsClassNotFoundException
, and NOT some wrapping unchecked exception.Many other examples on how to use it (after statically importing
UtilException
):But don't use it before understanding the following advantages, disadvantages, and limitations:
• If the calling-code is to handle the checked exception you MUST add it to the throws clause of the method that contains the stream. The compiler will not force you to add it anymore, so it's easier to forget it.
• If the calling-code already handles the checked exception, the compiler WILL remind you to add the throws clause to the method declaration that contains the stream (if you don't it will say: Exception is never thrown in body of corresponding try statement).
• In any case, you won't be able to surround the stream itself to catch the checked exception INSIDE the method that contains the stream (if you try, the compiler will say: Exception is never thrown in body of corresponding try statement).
• If you are calling a method which literally can never throw the exception that it declares, then you should not include the throws clause. For example: new String(byteArr, "UTF-8") throws UnsupportedEncodingException, but UTF-8 is guaranteed by the Java spec to always be present. Here, the throws declaration is a nuisance and any solution to silence it with minimal boilerplate is welcome.
• If you hate checked exceptions and feel they should never be added to the Java language to begin with (a growing number of people think this way, and I am NOT one of them), then just don't add the checked exception to the throws clause of the method that contains the stream. The checked exception will, then, behave just like an UNchecked exception.
• If you are implementing a strict interface where you don't have the option for adding a throws declaration, and yet throwing an exception is entirely appropriate, then wrapping an exception just to gain the privilege of throwing it results in a stacktrace with spurious exceptions which contribute no information about what actually went wrong. A good example is Runnable.run(), which does not throw any checked exceptions. In this case, you may decide not to add the checked exception to the throws clause of the method that contains the stream.
• In any case, if you decide NOT to add (or forget to add) the checked exception to the throws clause of the method that contains the stream, be aware of these 2 consequences of throwing CHECKED exceptions:
1) The calling-code won't be able to catch it by name (if you try, the compiler will say: Exception is never thrown in body of corresponding try statement). It will bubble and probably be catched in the main program loop by some "catch Exception" or "catch Throwable", which may be what you want anyway.
2) It violates the principle of least surprise: it will no longer be enough to catch RuntimeException to be able to guarantee catching all possible exceptions. For this reason, I believe this should not be done in framework code, but only in business code that you completely control.
In conclusion: I believe the limitations here are not serious, and the
UtilException
class may be used without fear. However, it's up to you!It can be resolved by below simple code with Stream and Try in AbacusUtil:
Disclosure: I'm the developer of
AbacusUtil
.Use #propagate() method. Sample non-Guava implementation from Java 8 Blog by Sam Beran:
You can potentially roll your own
Stream
variant by wrapping your lambda to throw an unchecked exception and then later unwrapping that unchecked exception on terminal operations:Then you could use this the same exact way as a
Stream
:This solution would require quite a bit of boilerplate, so I suggest you take a look at the library I already made which does exactly what I have described here for the entire
Stream
class (and more!).This does not directly answer the question (there are many other answers that do) but tries to avoid the problem in the first place:
In my experience the need to handle exceptions in a
Stream
(or other lambda expression) often comes from the fact that the exceptions are declared to be thrown from methods where they should not be thrown. This often comes from mixing business logic with in- and output. YourAccount
interface is a perfect example:Instead of throwing an
IOException
on each getter, consider this design:The method
AccountReader.readAccount(…)
could read an account from a database or a file or whatever and throw an exception if it does not succeed. It constructs anAccount
object that already contains all values, ready to be used. As the values have already been loaded byreadAccount(…)
, the getters would not throw an exception. Thus you can use them freely in lambdas without the need of wrapping, masking or hiding the exceptions.Of course it is not always possible to do it the way I described, but often it is and it leads to cleaner code altogether (IMHO):
throws IOException
for no use but to satisfy the compilerAccount
immutable and profit from the advantages thereof (e.g. thread safety)Account
in lambdas (e.g. in aStream
)