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