Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Choosing between overloaded methods if actual parameter is a lambda

In Java 1.8, the following lambda expression complies with both Runnable and Callable functional interfaces:

() -> {
    throw new RuntimeException("FIXME");
}

Still, if I submit it to an ExecutorService using a single-argument method, and ignore the return value (i. e. no type inference information is available), ExecutorService#submit(Callable) is chosen at compile time, unless I explicitly cast my lambda to Runnable.

How does the compiler choose between overloaded methods in the above case, provided that Runnable and Callable don't share any common hierarchy and most specific type rule doesn't apply here?

like image 892
Bass Avatar asked Oct 21 '15 14:10

Bass


2 Answers

I believe this is because Callable declares a return type, and Runnable does not.

From the JLS section 15.12.2.5, the overload with the most specific type is chosen, if there is one unambiguously most specific. This is what it says about most specific functional interface types:

A functional interface type S is more specific than a functional interface type T for an expression e if T is not a subtype of S and one of the following is true (where U1 ... Uk and R1 are the parameter types and return type of the function type of the capture of S, and V1 ... Vk and R2 are the parameter types and return type of the function type of T):

If e is an explicitly typed lambda expression (§15.27.1), then one of the following is true:

  • R2 is void...

T is Runnable, S is Callable, Callable is more specific because its return type is not void, therefore Callable is chosen

Method overload resolution is very complicated, so there may be a bit I've missed, but I think this is why it chooses Callable

like image 110
thecoop Avatar answered Oct 14 '22 01:10

thecoop


Although @thecoop's answer is correct, there is another aspect of the lambda mechanism worth mentioning.

Lambdas with block bodies fall into two categories: void-compatible and value-compatible.

The lambda in the question is both, because it cannot complete normally (due to the unconditional exception throw) and all the return statements in there are both valueless and returning a value, seeing as there's no return statement at all.

Most lambdas however are either one or the other. And if the lambda is only void-compatible, then the lambda will only be compatible with Runnable.

What is slightly counter-intuitive (but logically correct) is that even though the lambda in the question never returns a value, it is classified as "always returning a value when there's a return".

like image 39
biziclop Avatar answered Oct 13 '22 23:10

biziclop