The following approaches can be used to handle exceptions in Java lambda
Add
try-catch
block to the lambda statementExtract
try-catch
block to a wrapper methodCreate a generic method to wrap the
try-catch
block
Apart from that, you can throw unchecked exceptions to the lambda outside but can't throw checked exceptions
Let's walk through this article to explore them in more details
Add try-catch to the lambda statement
Use try-catch to handle exceptions in lambda is the most straightforward way to do
Say you use Files.readAllLines
to read all lines from a list of file, some of them may not exist which will throw IOException
List.of("test.txt", "test2.txt").forEach(item -> {
try {
Files.readAllLines(Path.of(item));
} catch (IOException e) {
System.out.println(e);
throw new RuntimeException(e);
}
});
As IOException
is a checked exception, you have to convert it to a RuntimeException
if you'd like to propagate it to the outside
Extract try-catch
block to a wrapper method
Add try-catch
block into a lambda can harm the readability. Lambda code should be concise, as it usually is a part of a chaining pipeline
The above try-catch block can be extracted to a separate method
void readFile(String fileName) throws RuntimeException{
try {
Files.readAllLines(Path.of(fileName));
} catch (IOException e) {
System.out.println(e);
throw new RuntimeException(e);
}
}
The lambda statement now is converted to expression form, concise and clean with a single line only
public void wrapperFunction() {
List.of("test.txt", "test2.txt")
.forEach(item -> readFile(item));
}
Generalize the extracted method
The above approach isn't bad, but you have to extract try-catch
block into a separate method each time
As forEach
accepts a built-in Consumer
functional interface which defined as the following
@FunctionalInterface
public interface Consumer<T> {
void accept(T t);
...
}
You can create a similar functional interface which throws Exception
on the single abstract method and a static method which accepts the custom interface, catch the Exception
and rethrows RuntimeException
as an alternate
@FunctionalInterface
interface ThrowingConsumer<T, E extends Exception> {
void accept(T t) throws E;
static <T> Consumer<T> wrapper(ThrowingConsumer<T, Exception> t) {
return arg -> {
try {
t.accept(arg);
} catch (Exception e) {
throw new RuntimeException(e);
}
};
}
}
Then put the lambda inside wrapper(ThrowingConsumer)
public void generalWrapperFunction() {
List.of("test.txt", "test2.txt")
.forEach(wrapper(item -> Files.readAllLines(Path.of(item))));
}
Conclusion
In this article, we learned to handle exceptions in Java lambda. You can find the full source code as below