I know how to create a reference to a method that has a String
parameter and returns an int
, it\'s:
Function
There are a lot of great responses already posted here. Just attempting to solve the problem with a different perspective. Its just my 2 cents, please correct me if I am wrong somewhere.
Throws clause in FunctionalInterface is not a good idea
I think this is probably not a good idea to enforce throws IOException because of following reasons
This looks to me like an anti-pattern to Stream/Lambda. The whole idea is that the caller will decide what code to provide and how to handle the exception. In many scenarios, the IOException might not be applicable for the client. For example, if the client is getting value from cache/memory instead of performing actual I/O.
Also, the exceptions handling in streams becomes really hideous. For example, here is my code will look like if I use your API
acceptMyMethod(s -> {
try {
Integer i = doSomeOperation(s);
return i;
} catch (IOException e) {
// try catch block because of throws clause
// in functional method, even though doSomeOperation
// might not be throwing any exception at all.
e.printStackTrace();
}
return null;
});
Ugly isn't it? Moreover, as I mentioned in my first point, that the doSomeOperation method may or may not be throwing IOException (depending on the implementation of the client/caller), but because of the throws clause in your FunctionalInterface method, I always have to write the try-catch.
What do I do if I really know this API throws IOException
Then probably we are confusing FunctionalInterface with typical Interfaces. If you know this API will throw IOException, then most probably you also know some default/abstract behavior as well. I think you should define an interface and deploy your library (with default/abstract implementation) as follows
public interface MyAmazingAPI {
Integer myMethod(String s) throws IOException;
}
But, the try-catch problem still exists for the client. If I use your API in stream, I still need to handle IOException in hideous try-catch block.
Provide a default stream-friendly API as follows
public interface MyAmazingAPI {
Integer myMethod(String s) throws IOException;
default Optional myMethod(String s, Consumer super Exception> exceptionConsumer) {
try {
return Optional.ofNullable(this.myMethod(s));
} catch (Exception e) {
if (exceptionConsumer != null) {
exceptionConsumer.accept(e);
} else {
e.printStackTrace();
}
}
return Optional.empty();
}
}
The default method takes the consumer object as argument, which will be responsible to handle the exception. Now, from client's point of view, the code will look like this
strStream.map(str -> amazingAPIs.myMethod(str, Exception::printStackTrace))
.filter(Optional::isPresent)
.map(Optional::get).collect(toList());
Nice right? Of course, logger or other handling logic could be used instead of Exception::printStackTrace.
You can also expose a method similar to https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/CompletableFuture.html#exceptionally-java.util.function.Function- . Meaning that you can expose another method, which will contain the exception from previous method call. The disadvantage is that you are now making your APIs stateful, which means that you need to handle thread-safety and which will be eventually become a performance hit. Just an option to consider though.