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 <T> void test(List<T> list0, List<T> 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<T>,List<T> found: List<CAP#1>,List<CAP#2> reason: no instance(s) of type variable(s) T exist so that argument type List<CAP#2> conforms to formal parameter type List<T> where T is a type-variable: T extends Object declared in method <T>test(List<T>,List<T>) 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<CAP#1> empty = Collections.<CAP#1>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.
What is the inferred type in this case? Is it Object? Or it doesn't really matter due to the wildcard telling the compiler any type is possible?
On one level, it's kind of a philosophical question, because the type argument does not have any effect on the compiled bytecode, so it doesn't really matter what it is specifically. The only thing that matters is whether or not it's impossible to satisfy the bounds and context. As long as the compiler can prove that there exists some type that could work, then in my opinion, it should be able to go ahead and compile it without needing to come up with an actual type.