Inferring Exception type of a lambda body

Zhong Yu zhong.j.yu at gmail.com
Tue Feb 5 10:16:21 PST 2013


Say we have a functional interface in which the method throws a type variable E

    interface Action<E extends Throwable>
    {
        void doIt() throws E;
    }

and a generic method that invokes Action<E> and passes through E

    <E extends Throwable> void invoke(Action<E> action) throws E
    {
        action.doIt(); // throws E
    }

It works for lambdas that throw checked exceptions:

    // compiles, E is inferred as Exception
    void test1() throws Exception
    {
        invoke( ()->{ throw new Exception(); } );
    }

But it doesn't work for unchecked exceptions:

    // fails, E is inferred as Throwable
    void test2()
    {
        invoke( ()->{ } );
        // or invoke( ()->{ throw new RuntimeException(); } );
        // or invoke( ()->{ throw new Error(); } );
    }

Apparently, if the lambda body throws only unchecked exceptions, javac
does not set a lower bound on E, so E is chosen to be its upper bound
Throwable.

I'd argue that Throwable is too broad for E, and E should have a lower
bound. Since any method can throw Error, Action.doIt() actually throws
Error|E, which should be a super type of unchecked exceptions, i.e.
Error|E :> Error|RuntimeException, so we can argue that E :>
RuntimeException.

My suggestion for the inference rule is, very simplistically,

If E has not been inferred from previous steps, and E is in the throw
clause, and E has an upper constraint E<<X,
    if X:>RuntimeException, infer E=RuntimeException
    otherwise, infer E=X. (X is an Error or a checked exception)

Zhong Yu


More information about the lambda-dev mailing list