Java Generics: question about type capture and inference generated using generic methods

advertisements

This is a follow-up to my previous question but since the previous thread was a long one, i decided to start another thread pertaining to the almost same topic.

public class GenericMethodInference {

static <T> void test1(T t1, T t2) {}
static <T> void test3(T t1, List <T> t2) {}
static <T> void test4(List <T> t1, List <T> t2) {}

public static void main(String [] args) {

    List <Object> c = new LinkedList<Object>();
    List <? extends Object> d = new ArrayList<Integer>();
    List e = new ArrayList<Integer>();

    test1("Hello", new Integer(1)); // ok clause (1)
    GenericMethodInference.<Object>test1("Hello", new Integer(1)); // ok clause (2)
    test3("Hello", c); // ok clause (3)
    test4(d,d) // clause (4) Error due to different type capture generated

}

Note: If you move your cursor over each clause, you will see the inference being generated and displayed on Eclipse:

a. Clause (1) will produce <? extends Object> test1 <? extends Object, ? extends Object>
b. Clause (2) will produce exactly what's defined in the actual type parameter
c. Clause (3) will produce <Object> test3 <Object, List <Object>>

Questions:

  1. Why clause (1) didn't produce <Object>? Since <Object> works as shown in clause (2), why <? extends Object> being produce instead?
  2. why clause (3) produce <Object> instead of <? extends Object>?
  3. Since clause (4) uses the same variable, why 2 different type capture generated eventhough the parameter used is of the same variable d?

Why clause (1) didn't produce <Object>? Since <Object> works as shown in clause (2), why <? extends Object> being produce instead?

This is the best question out of the three. My thinking is that the compiler/Eclipse doesn't want to assume that Object is necessarily the type T that is inferred between String and Integer, so it plays it safe. As @bringer128 pointed out, String and Integer also both implement Serializable and Comparable - so these types are also candidates for the inferred type of the method.

It's worth noting that the following code gives the compiler error "illegal start of type":

GenericMethodInference.<? extends Object>test1("Hello", new Integer(1));

This is because it's invalid to specify a wildcard as a method's type parameter. So the fact you're seeing that in the tooltip has to do with a subtlety of the compiler's/Eclipse's facility to report this information - it has determined only that T is within its bounds, not what it is.

Remember that Java's implementation of generics is solely for the convenience/sanity of programmers. Once compiled into bytecode, type erasure will have gotten rid of any notion of T. So in its checking, the compiler only needs to ensure that a valid T can be inferred, but not necessarily what it is.


why clause (3) produce <Object> instead of <? extends Object>?

Because in this case, the fact that a List<Object> is passed in where a List<T> is expected tells the compiler that T is exactly Object.


Since clause (4) uses the same variable, why 2 different type capture generated eventhough the parameter used is of the same variable d?

It isn't safe for the compiler to assume that d actually refers to the same object, even between evaluating parameters. For example:

test4(d,(d = new ArrayList<String>()));

In this case, a List<Integer> would be passed into the first parameter, and an List<String> into the second - both from d. Since this scenario is possible, it's easier for the compiler to play it safe.