Checked exceptions in lambdas in Java
Standard functional interfaces cannot throw checked exceptions.
Function made a promise
The standard functional interfaces (Function, Consumer, Supplier...) declare no checked exceptions. Function.apply promises its callers it won't throw IOException, so a lambda implementing it can't let one escape.
public interface Function<T, R> {
R apply(T t); // no "throws" here
}Your turn
load never really throws. What happens?
static String load(String p)
throws IOException { return "data:" + p; }
void main() {
Function<String, String> f = p -> load(p);
System.out.println(f.apply("x"));
}Prints data:xCompile errorThrows IOException
Show the answer
Compile error. load *declares* IOException, and checked exceptions are verified from declarations at compile time, not from what happens at runtime.
Unchecked exceptions are fine
Only checked exceptions must be declared. **RuntimeException and its subclasses (IllegalArgumentException, NullPointerException...) can be thrown from any** lambda.
Function<Integer, Integer> half = n -> {
if (n % 2 != 0)
throw new IllegalArgumentException();
return n / 2;
}; // ✓ compilesFix 1: catch and wrap
Handle it inside the lambda: catch the checked exception and rethrow it unchecked, typically as **UncheckedIOException**. The error stays visible; it just travels as a RuntimeException.
Function<String, String> f = p -> {
try { return load(p); }
catch (IOException e) {
throw new UncheckedIOException(e);
}
};Fixes that don't work
void main() throws IOException {
Function<String, String> f =
p -> load(p); // ✗
}The exception would be thrown inside apply, not by main. A cast doesn't help either: it can't change apply's signature.
Function<String, String> f = p -> {
try { return load(p); }
catch (IOException e) {
throw new UncheckedIOException(e);
}
};Caught inside the lambda and rethrown unchecked.
Fix 2: your own interface
Define a functional interface whose method **declares throws IOException. Now the lambda may let it escape, and the caller** handles it.
@FunctionalInterface
interface IOFunc<T, R> {
R apply(T t) throws IOException;
}Custom interface
What does this print?
interface IOFunc<T, R> {
R apply(T t) throws IOException;
}
static String load(String p)
throws IOException { return "data:" + p; }
void main() throws IOException {
IOFunc<String, String> f = p -> load(p);
System.out.println(f.apply("x"));
}Compile errordata:xThrows IOException
Show the answer
IOFunc.apply declares IOException, so the lambda may throw it, and main declares it too. Nothing actually throws, so it prints data:x.
Real-world friction
Reading files, calling JDBC or parsing inside streams runs straight into this. Teams usually wrap with UncheckedIOException close to the I/O call, or use small "throwing function" interfaces from a utility library. Either way, never swallow the exception silently.
Key takeaways
- Function.apply declares no checked exceptions
- Unchecked exceptions (RuntimeException) are always allowed
- Fix 1: try/catch inside the lambda and rethrow unchecked
- Fix 2: a custom @FunctionalInterface with 'throws IOException'
UncheckedIOException was added in Java 8, the same release as lambdas. BufferedReader.lines() uses it to report I/O errors from inside a stream, where a checked exception couldn't escape.
Practice questions
What does this print?
static String read(String p) throws IOException {
return "data:" + p;
}
void main() {
Function<String, String> f = p -> read(p);
System.out.println(f.apply("a.txt"));
}- data:a.txt
- Compile error
- Throws IOException
- Throws UncheckedIOException
Check your answer
Compile error. read declares IOException, but the lambda implements Function.apply, which doesn't. The compiler reports an unreported exception — even though read never actually throws here.
Which change makes Function<String, String> f = p -> read(p); compile while keeping the error visible?
- Add throws IOException to main
- Wrap the call: p -> { try { return read(p); } catch (IOException e) { throw new UncheckedIOException(e); } }
- Cast the lambda: (Function<String, String>) p -> read(p)
- Use Runnable instead of Function
Check your answer
Wrap the call: p -> { try { return read(p); } catch (IOException e) { throw new UncheckedIOException(e); } }. Catching inside the lambda and rethrowing an unchecked exception satisfies Function.apply. A throws clause on main doesn't help because the exception is thrown inside apply, and a cast doesn't change apply's signature.