Java Generics: a question on type capture and output generation using common methods

This is a continuation of my previous question, but since the previous thread was long, I decided to start another thread related to almost the 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 the cursor over each sentence, you will see that the output is created and displayed in Eclipse:

but. Item (1) will produce <? extends Object> test1 <? extends Object ,? extends the object>
b. Paragraph (2) will produce exactly what is defined in the actual parameter of type c. In paragraph (3) test3> will be created

Questions:

  • Why didn't sentence (1) create? Since it works, as shown in paragraph (2), why <? extends Object> instead?
  • why does sentence (3) create instead of <? extends Object>?
  • Since in the sentence (4) the same variable is used, why is 2 a different type generated, while the parameter used has the same variable d?
+7
source share
2 answers

Why sentence (1) did not return <Object>? Since <Object> works as shown in point (2), why <? extends Object> instead?

This is the best question of the three. I think the / Eclipse compiler does not want to assume that Object necessarily a type T , which is derived between String and Integer , so it is safe. As noted in @ bringer128 , String and Integer also implement Serializable and Comparable - so these types are also candidates for the intended type of method.

It is worth noting that the following code gives a compiler error of "illegal type startup":

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

This is due to the fact that it is not valid for specifying a template as a parameter of a method type. Thus, the fact that you see that in the tooltip is related to the subtleties of the compiler / Eclipse means for reporting this information - he only determined that T is within its boundaries, and not what it is.

Remember that the implementation of Java generics is intended solely for the convenience of programmers. Once compiled into bytecode, type erasure will get rid of any T concept. Therefore, when checking, the compiler needs to make sure that a valid T can be inferred, but it is not necessary what it is.


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

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


Since the sentence (4) uses the same variable, why is the 2nd type captured by the generated event, although the parameter used has the same variable d?

It is not safe for the compiler to assume that d actually refers to the same object, even between parameter evaluations. For example:

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

In this case, a List<Integer> will be passed to the first parameter, and List<String> to the second - as from d . Since this scenario is possible, it is easier for the compiler to play safely.

+4
source

The case test1 () is actually quite ominous. see JLS3 12.15.2.7.

We do not need to know the details of type inference - in most cases, intuition coincides with the algorithm. Alas, this is not always the case as in the seemingly trivial example test1 ().

The limitations we have are T :> String and T :> Integer (" :> " means super-type)

This results in T=lub(String,Integer) , lub means "smallest upper bound".

Since String <: Comparable<String> and Integer <: Comparable<Integer> , this leads to lci({Comparable<String>, Comparable<Integer>}) , which gives Comparable<? extends lub(String,Integer)> Comparable<? extends lub(String,Integer)> , i.e. Compable<? extends T>

In the end, we have T = Serializable & Compable<? extends T> T = Serializable & Compable<? extends T> self-defined definition! Spec calls it the "infinite type":

It is possible that the above process gives an infinite type. This is acceptable, and Java compilers should recognize such situations and represent them accordingly using circular data structures.

Let's find out how javac represents this: (javac 7)

 static <T> T test1(T t1, T t2) {} public static void main(String[] args) { Void x = test1("Hello", new Integer(1)); } error: incompatible types required: Void found: INT#1 where INT#1,INT#2 are intersection types: INT#1 extends Object,Serializable,Comparable<? extends INT#2> INT#2 extends Object,Serializable,Comparable<?> 

This does not seem right; it is not recursive; it seems that javac detects recursion in lub() and refuses, resulting in a less specific type Comparable<?>

+2
source

All Articles