λ Lambdas & Functional Java · Intermediate

Checked exceptions in lambdas in Java

Standard functional interfaces cannot throw checked exceptions.

🧩 The mysteryYour helper method never actually throws. You call it inside a lambda. Compile error: "unreported exception IOException". The compiler isn't wrong. Here's why.

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
}
🔮 Predict it

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"));
}
  1. Prints data:x
  2. Compile error
  3. Throws 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;
};  // ✓ compiles

Fix 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

✗ Still an error
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.

✓ Compiles
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;
}
🔮 Predict it

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"));
}
  1. Compile error
  2. data:x
  3. Throws 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.

💼 In the real world

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

  1. Function.apply declares no checked exceptions
  2. Unchecked exceptions (RuntimeException) are always allowed
  3. Fix 1: try/catch inside the lambda and rethrow unchecked
  4. Fix 2: a custom @FunctionalInterface with 'throws IOException'
🤯 Did you know?

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"));
}
  1. data:a.txt
  2. Compile error
  3. Throws IOException
  4. 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?

  1. Add throws IOException to main
  2. Wrap the call: p -> { try { return read(p); } catch (IOException e) { throw new UncheckedIOException(e); } }
  3. Cast the lambda: (Function<String, String>) p -> read(p)
  4. 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.

Next: what if a method takes a function, or hands one back? Higher-order functions turn behaviour into data.