Hi Salil,
that's a good question, as it's a common problem with Streams, Optional pipelines, or just when trying to write a compact lambda, and I discuss the possible alternatives to try-catch you listed.
The first option is not throwing Exceptions in the first place by extracting the offending lambda into a "safe method", handling the Exception there, and returning null or an Optional, depending on the other steps in the Stream. You can often design the safe method as a method reference to gain at least better readability.
The downside is that you don't have access to the Exception, as the safe method does all the handling.
The second option, an Either type, is a good compromise, as you have both access to the value and the possible Exception.
I develop such a custom tuple as a generic Record in the chapter 10:
The Result type has multiple helper methods to handle success and failure, but also a "orElseThrow()" method like Optional<T>.
This one uses a feature called "sneaky throws" that allows a method to throw a checked exception without declaring it in the signature.
It's a
quirk of the JVM when it has to deal with generic Exceptions.
In this case, though, I think it's an acceptable trade-off.
The last option I show is how to replicate the try/success/failure
pattern from Scala.
The end result is still not pretty, though:
An additional option might be CompletableFutures, as they provide error-handling for declarative pipelines of higher-order functions.
But it's still not the simple solution we want...
So what should we do regarding Exceptions and functional Java code?
Well, it highly depends on the context, and API you're using, and none of the options is 100% satisfying...
I usually try to refactor the code to extract a "safe method" with more general exception handling.
But I also have to accept sometimes that some APIs are just not "functional enough" to be easily used with the newer Java features.
If it doesn't fit, don't force it.
Also, there are several third-party libraries out there, like
Vavr or
jOOλ, that allow you to uncheck Exceptions and eas the pain of handling Exceptions in lambdas.
However, I don't recommend adding such a dependency just to simplify Exceptions, as any dependency adds a potential long-term commitment to your project.