javaintermediate

Checked Exception Wrapping Utilities

Wrap checked exceptions for use in lambdas and streams: sneaky throws, functional wrappers, and Either.

java
import java.util.*;
import java.util.function.*;
import java.util.stream.*;

public class CheckedExceptionUtils {

    // Functional interfaces that allow checked exceptions
    @FunctionalInterface
    public interface CheckedFunction<T, R> {
        R apply(T t) throws Exception;
    }

    @FunctionalInterface
    public interface CheckedSupplier<T> {
        T get() throws Exception;
    }

    @FunctionalInterface
    public interface CheckedConsumer<T> {
        void accept(T t) throws Exception;
    }

    // Wrap checked → unchecked
    public static <T, R> Function<T, R> unchecked(CheckedFunction<T, R> fn) {
        return t -> {
            try { return fn.apply(t); }
            catch (Exception e) { throw new RuntimeException(e); }
        };
    }

    public static <T> Supplier<T> unchecked(CheckedSupplier<T> fn) {
        return () -> {
            try { return fn.get(); }
            catch (Exception e) { throw new RuntimeException(e); }
        };
    }

    public static <T> Consumer<T> unchecked(CheckedConsumer<T> fn) {
        return t -> {
            try { fn.accept(t); }
            catch (Exception e) { throw new RuntimeException(e); }
        };
    }

    // Either type for error-as-value
    public sealed interface Either<L, R> {
        record Left<L, R>(L value) implements Either<L, R> {}
        record Right<L, R>(R value) implements Either<L, R> {}

        static <L, R> Either<L, R> left(L value) { return new Left<>(value); }
        static <L, R> Either<L, R> right(R value) { return new Right<>(value); }

        default <T> T fold(Function<L, T> onLeft, Function<R, T> onRight) {
            return switch (this) {
                case Left<L, R> l -> onLeft.apply(l.value);
                case Right<L, R> r -> onRight.apply(r.value);
            };
        }
    }

    // Try with Either (errors as values)
    public static <T, R> Function<T, Either<Exception, R>> trying(
            CheckedFunction<T, R> fn) {
        return t -> {
            try { return Either.right(fn.apply(t)); }
            catch (Exception e) { return Either.left(e); }
        };
    }

    public static void main(String[] args) {
        // Use checked functions in streams
        List<String> urls = List.of("file1.txt", "file2.txt", "missing.txt");

        // Wrap for stream use
        List<String> contents = urls.stream()
            .map(unchecked(name -> {
                if (name.equals("missing.txt")) throw new java.io.FileNotFoundException(name);
                return "content of " + name;
            }))
            .toList();

        // Either approach — collects errors instead of throwing
        var results = urls.stream()
            .map(trying(name -> {
                if (name.equals("missing.txt")) throw new java.io.FileNotFoundException(name);
                return "content of " + name;
            }))
            .toList();

        results.forEach(either -> {
            String msg = either.fold(
                err -> "ERROR: " + err.getMessage(),
                ok -> "OK: " + ok
            );
            System.out.println(msg);
        });
    }
}

Use Cases

  • Using checked-exception methods in lambda expressions
  • Stream processing with fallible operations
  • Functional error handling without try-catch

Tags

Related Snippets

Similar patterns you can reuse in the same workflow.