Skip to content
fge edited this page Dec 30, 2014 · 8 revisions

Interfaces...

Credits where they are due: thanks to Vincentyfication for the design idea

For each functional interface Foo usable in Streams, this package defines an interface ThrowingFoo. For instance, here is the base code for the interface ThrowingFunction:

@FunctionalInterface
public interface ThrowingFunction<T, R>
    extends Function<T, R>,
    ThrowingFunctionalInterface<ThrowingFunction<T, R>, Function<T, R>>
{
    R doApply(T t)
        throws Throwable;

    @Override
    default R apply(T t)
    {
        try {
            return doApply(t);
        } catch (Error | RuntimeException e) {
            throw e;
        } catch (Throwable tooBad) {
            throw new ThrownByLambdaException(tooBad);
        }
    }
}

Note that it extends the base interface; any implementation (class, method reference, lambda...) is therefore also usable as the base type.

ThrowingFunctionalInterface

All "throwing interfaces" extend this interface, whose code follows; the javadoc should be enough to let you understand what each of these methods do:

public interface ThrowingFunctionalInterface<T extends N, N>
{
    /**
     * Chain this throwing lambda with another throwing lambda if this one fails
     *
     * <p>This means that if this lambda fails with an unchecked exception, the
     * second one will be tried instead.</p>
     *
     * <p>Since this method itself returns a throwing lambda, it means you can
     * chain more than two lambdas this way: {@code
     * l1.orTryWith(l2).orTryWith(l3)} etc.</p>
     *
     * @param other the other throwing lambda
     * @return a new throwing lambda
     */
    T orTryWith(T other);

    /**
     * Chain this throwing lambda with a non throwing lambda fallback
     *
     * <p>If this throwing lambda fails with a checked exception, the fallback
     * (which cannot throw checked exceptions) is invoked instead.</p>
     *
     * <p>As this method returns a non throwing version, you cannot chain any
     * behavior afterwards.</p>
     *
     * @param fallback the fallback
     * @return a non throwing lambda
     */
    N fallbackTo(N fallback);

    /**
     * Change the unchecked exception thrown by this throwing lambda
     *
     * <p>This allows to change the exception which is thrown if this throwing
     * lambda throws a checked exception; as for the default behavior, the
     * exception thrown by the lambda is set as the cause of an instance of this
     * exception class.</p>
     *
     * <p>See {@link ThrowablesFactory} for more details.</p>
     *
     * <p>As this method returns a non throwing version, you cannot chain any
     * behavior afterwards.</p>
     *
     * @param exceptionClass the exception class
     * @param <E> the type of this exception class
     * @return a non throwing lambda
     */
    <E extends RuntimeException> N orThrow(Class<E> exceptionClass);

    /**
     * Alias to {@link #orThrow(Class)}
     *
     * <p>This alias is here purely for convenience. It allows you to write:</p>
     *
     * <pre>
     *     rethrow(something).as(SomeException.class)
     * </pre>
     *
     * @param exceptionClass the exception class
     * @param <E> the type of this exception class
     * @return a non throwing lambda
     *
     * @see #orThrow(Class)
     */
    default <E extends RuntimeException> N as(Class<E> exceptionClass)
    {
        return orThrow(exceptionClass);
    }
}

The wrappers

The wrapper methods are defined in a utility class and simply wraps a ThrowingFoo into a Foo. For a Function, this is defined in class Functions:

public static <T, R> ThrowingFunction<T, R> wrap(final ThrowingFunction<T, R> f)
{
    return f;
}

The primary usage of these wrapping methods are in Streams; for instance this:

// ERROR!
.map(Path::toRealPath)

will not work; this, however, does:

// OK, since ThrowingFunction extends Function
.map(Functions.wrap(Path::toRealPath))
Clone this wiki locally