Lambda expressions can throw exceptions, but the functional interface's abstract method signature governs which checked exceptions are allowed. If the interface doesn't declare a checked exception, the lambda must handle it internally with try-catch or wrap it as an unchecked exception.
Key Points: • Standard functional interfaces like Function, Consumer, and Supplier don't declare any checked exceptions in their abstract methods. • A lambda implementing one of those interfaces cannot throw a checked exception without catching it first, or the code won't compile. • Unchecked exceptions (RuntimeException and subclasses) can be thrown freely from any lambda without special handling. • A common pattern is wrapping a checked exception inside a RuntimeException (or a custom unchecked exception) so it can propagate out of the lambda. • For reusable checked-exception handling, developers often define a custom functional interface whose method declares throws Exception, then adapt it to the standard interface with a wrapper.
Example: A lambda passed to Stream.map() that calls a method throwing IOException must catch the IOException inside the lambda body, since Function.apply() doesn't declare it.
Code Example:
List<String> paths = getPaths();
List<String> contents = paths.stream()
.map(path -> {
try {
return Files.readString(Path.of(path));
} catch (IOException e) {
throw new RuntimeException(e);
}
})
.collect(Collectors.toList());Interview Tip: A concise interview answer is:
"Lambdas can throw exceptions, but only what the functional interface's method signature allows. Since interfaces like Function or Consumer don't declare checked exceptions, I have to catch a checked exception inside the lambda, typically wrapping it in an unchecked exception so it can still propagate out of the stream pipeline."