Skip to content
Jeffrey Falgout edited this page Jun 16, 2015 · 10 revisions

Java has two types of exceptions: checked and unchecked. If you want to throw a checked exception, you have to say so in your method signature. You're allowed to throw an unchecked exception whenever you want.

This library works by exploiting this system. It catches checked exceptions and rethrows them as unchecked exceptions so that the method signature is compatible with that of java.util.stream.Stream. Then, in a terminal operation, it catches the unchecked exception and rethrows it as the checked exception.

Here's a simplified example:

class StreamBridge<T, X extends Throwable> implements ThrowingStream<T, X> {
    private static class BridgeException extends RuntimeException {
        public BridgeException(Throwable cause) {
            super(cause);
        }
    }

    private final Stream<T> delegate;
    private final Class<X> x;

    StreamBridge(Stream<T> delegate, Class<X> x) {
        this.delegate = delegate;
        this.x = x;
    }

    private <R> R maskException(ThrowingSupplier<R, X> method) {
        try {
            return method.get();
        } catch (Throwable t) {
            if (x.isInstance(t)) {
                throw new BridgeException(t);
            } else {
                throw t;
            }
        }
    }

    @Override
    public ThrowingStream<T, X> filter(ThrowingPredicate<T, X> predicate) {
        return new StreamBridge<>(
                delegate.filter(t -> maskException(() -> predicate.test(t))), x);
    }

    @Override
    public <R> ThrowingStream<R, X> map(ThrowingFunction<T, R, X> mapper) {
        return new StreamBridge<>(
                delegate.map(t -> maskException(() -> mapper.apply(t))), x);
    }

    private <R> R unmaskBridgeException(Supplier<R> method) throws X {
        try {
            return method.get();
        } catch (BridgeException e) {
            throw x.cast(e.getCause());
        }
    }

    @Override
    public <A, R> R collect(Collector<T, A, R> collector) throws X {
        return unmaskBridgeException(() -> delegate.collect(collector));
    }
}

The two important methods here are maskException and unmaskBridgeException. launder catches a checked exception and cleans it up by wrapping it in a BridgeException. unmaskBridgeException does the opposite: it catches BridgeExceptions and rethrows the original, generic exception.

Clone this wiki locally