Skip to content
Jeffrey Falgout edited this page Jun 17, 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 StreamAdapter<T, X extends Throwable> implements ThrowingStream<T, X> {
    private static class AdapterException extends RuntimeException {
        public AdapterException(Throwable cause) {
            super(cause);
        }
    }

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

    StreamAdapter(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 AdapterException(t);
            } else {
                throw t;
            }
        }
    }

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

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

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

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

The two important methods here are maskException and unmaskException. maskException catches a checked exception and masks it up by wrapping it in a AdapterException. unmaskException does the opposite: it catches AdapterExceptions and rethrows the original, generic exception.

Clone this wiki locally