// Callable -> why is it not a Supplier? It does not throw any exceptions..
execute(() -> null);
This is because both the Callable<Void>
method and the Supplier<T>
method are applicable, but the former is more specific. You can see that this is the case by having only one of the two methods, and execute(() -> null);
will call that method.
To show that execute(Callable<Void>)
is more specific than execute(Supplier<T>)
, we'll have to go to §18.5.4, since the latter is a generic method.
Let m1 be the first method and m2 be the second method. Where m2 has type parameters P1, ..., Pp, let α1, ..., αp be inference variables, and let θ be the substitution [P1:=α1, ..., Pp:=αp].
Let e1, ..., ek be the argument expressions of the corresponding invocation. Then:
- If m1 and m2 are applicable by strict or loose invocation (§15.12.2.2, §15.12.2.3), then let S1, ..., Sk be the formal parameter types of m1, and let T1, ..., Tk be the result of θ applied to the formal parameter types of m2.
- [...]
So m1
is execute(Callable<Void>)
, and m2
is execute(Supplier<T>)
. P1
is T
. For the invocation execute(() -> null);
, e1
is () -> null
, and T
is inferred to be Object
, so α1
is Object
. T1
is then Supplier<Object>
. S1
is Callable<Void>
.
Now quoting only the parts relevant to the question:
The process to determine if m1 is more specific than m2 is as follows:
First, an initial bound set, B, is constructed from the declared bounds of P1, ..., Pp, as specified in §18.1.3.
Second, for all i (1 ≤ i ≤ k), a set of constraint formulas or bounds is generated.
Otherwise, Ti is a parameterization of a functional interface, I. It must be determined whether Si satisfies the following five conditions:
[...]
If all five conditions are true, then the following constraint formulas or bounds are generated (where U1 ... Uk and R1 are the parameter types and return type of the function type of the capture of Si, and V1 ... Vk and R2 are the parameter types and return type of the function type of Ti):
- If ei is an explicitly typed lambda expression:
- [...]
- Otherwise, ‹R1 <: R2›.
Note that a lambda with no parameters is an explicitly typed lambda.
Applying this back to your question, R1
is Void
, R2
is Object
, and the constraint ‹R1 <: R2›
says that Void
(not the lowercase void
) is a subtype of Object
, which is correct and is not contradictory.
Finally:
Fourth, the generated bounds and constraint formulas are reduced and incorporated with B to produce a bound set B'.
If B' does not contain the bound false, and resolution of all the inference variables in B' succeeds, then m1 is more specific than m2.
Since the constraint ‹Void <: Object›
is not contradictory, there is no false
constraint, and so execute(Callable<Void>)
is more specific than execute(Supplier<T>)
.
// Supplier -> this returns an Object, but how is that different from returning null?
execute(() -> new Object());
In this case, only the Supplier<T>
method is applicable. Callable<Void>
expects you to return something compatible with Void
, not Object
.
// Callable -> because it can throw an exception, right?
execute(() -> {throw new Exception();});
Not quite. Throwing an exception made the Callable<Void>
overload applicable, but the Runnable
overload is still applicable too. The reason why the former is chosen is still because Callable<Void>
is more specific than Runnable
for the expression () -> { throw new Exception(); }
(relevant parts only):
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:
Basically, any non-void
-returning functional interface type is more specific than a void
-returning functional interface type, for explicitly typed lambdas.