I understand that the compiler uses the target type to determine the type argument that makes the generic method invocation applicable. For instance, in the following statem
Each usage of a wildcard has a distinct type associated with it. (Usually the JLS refers to this as being a "fresh type".) This is how for example a compiler error like this works:
List> list0 = ... ;
List> list1 = ... ;
list0.add(list1.get(0)); // error
Because it's the case that list0
and list1
are given separate types by the compiler such that for the most part
reference_type_of(List>) != reference_type_of(List>)
You can begin to see how this fits in to type inference if you try something like
{
List> list0 = ... ;
List> list1 = ... ;
test(list0, list1);
}
static void test(List list0, List list1) {}
Where the compiler emits an error that actually tells us a little bit about the types that it has generated for list0
and list1
.
error: method test in class Ideone cannot be applied to given types; test(list0, list1); ^ required: List,List found: List ,List reason: no instance(s) of type variable(s) T exist so that argument type Listconforms to formal parameter type List where T is a type-variable: T extends Object declared in method test(List ,List ) where CAP#1,CAP#2 are fresh type-variables: CAP#1 extends Object from capture of ? CAP#2 extends Object from capture of ?
(My emphasis in bold.) These CAP#...
types were generated during capture conversion. What it's showing us is that when the method invocation expression was examined, list0
and list1
were given distinct types from each other. (And for those that need an explanation for the error: it's because the declaration of test
asserts that both Lists must have the same T
.)
So since we now know that a wildcard gets associated a reference type, we can see that in a case like
List> empty = Collections.emptyList();
The invocation will be inferred as something like "a fresh type where the upper bound is Object". Or symbolically we could say the compiler might see something like
// target type --> inferred invocation type
// v v
List empty = Collections.emptyList();
Although: of course we are always guessing a little bit because it's up to the compiler how it implements this. In theory, for a case like the above trivial assignment of emptyList()
, it would not have to do work to generate the correct bytecode.
Also, I am sorry, I don't feel like spec scouring today. Essentially the type inference here works by generating a set of constraints to demonstrate that the method invocation should or should not compile. The algorithm described in 18.5.2 incorporates a wildcard in this way.