If my understanding of chapters 15 and 18 of the JLS for Java SE 8 is correct, the key to your question lies in the following quote from paragraph 15.12.2:
Certain argument expressions that contain implicitly typed lambda expressions (§15.27.1) or inexact method references (§15.13.1) are ignored by the applicability tests, because their meaning cannot be determined until a target type is selected.
When a Java compiler encounters a method call expression such as test(() -> "test")
, it has to search for accessible (visible) and applicable (i.e. with matching signature) methods to which this method call can be dispatched. In your first example, both <T> void test(T)
and <T> void test(Supplier<T>)
are accessible and applicable w.r.t. the test(() -> "test")
method call. In such cases, when there are multiple matching methods, the compiler attempts to determine the most specific one. Now, while this determination for generic methods (as covered in
JLS 15.12.2.5 and JLS 18.5.4) is quite complicated, we can use the intuition from the opening of 15.12.2.5:
The informal intuition is that one method is more specific than another if any invocation handled by the first method could be passed on to the other one without a compile-time error.
Since for any valid call to <T> void test(Supplier<T>)
we can find a corresponding instantiation of the type parameter T
in <T> void test(T)
, the former is more specific than the latter.
Now, the surprising part is that in your second example, both <T> void test(Class<T>, Supplier<T>)
and <T> void test(Class<T>, T)
are considered applicable for method call test(String.class, () -> "test")
, even though it's clear to us, that the latter shouldn't be. The problem is, that the compiler acts very conservatively in the presence of implicitly typed lambdas, as quoted above. See in particular JLS 18.5.1:
A set of constraint formulas, C, is constructed as follows.
...
- To test for applicability by strict invocation:
If k ≠ n, or if there exists an i (1 ≤ i ≤ n) such that e_i is pertinent to applicability (§15.12.2.2) (...) Otherwise, C includes, for all i (1 ≤ i ≤ k) where e_i is pertinent to applicability, ?e_i → F_i θ?.
- To test for applicability by loose invocation:
If k ≠ n, the method is not applicable and there is no need to proceed with inference.
Otherwise, C includes, for all i (1 ≤ i ≤ k) where e_i is pertinent to applicability, ?e_i → F_i θ?.
and JLS 15.12.2.2:
An argument expression is considered pertinent to applicability for a potentially applicable method m unless it has one of the following forms:
- An implicitly typed lambda expression (§15.27.1).
...
So, the constraints from implicitly typed lambdas passed as arguments take no part in resolving type inference in the context of method applicability checks.
Now, if we assume that both methods are applicable, the problem - and the difference between this and the previous example - is that none of this methods is more specific. There exist calls which are valid for <T> void test(Class<T>, Supplier<T>)
but not for <T> void test(Class<T>, T)
, and vice versa.
This also explains why test(String.class, (Supplier<String>) () -> "test");
compiles, as mentioned by @Aominè in the comment above. (Supplier<String>) () -> "test")
is an explicitly typed lambda, and as such is considered pertinent to applicability, the compiler is able to correctly deduce, that only one of these methods is applicable, and no conflict occurs.