In each case, you created a little straw man by creating a LinkedList<Object> . This can be difficult. What you should remember is that when the compiler receives these method calls, it does not know that you created the LinkedList<Object> . For example, it could be a LinkedList<Integer> .
So, look at your code with more interesting initializations:
List<Integer> integers = new LinkedList<Integer>(); List <?> a = integers; List <? extends Object> b = integers; List <? super Object> c = new LinkedList<Object>(); //INVALID. T maps to a type that could be Object OR anything else. "Hello" //would only be type-assignable to T if T represented String, Object, CharSequence, //Serializable, or Comparable abc(a, "Hello"); //INVALID. T maps to a type that could be Object OR anything else. "Hello" //would only be type-assignable to T if T represented String, Object, CharSequence, //Serializable, or Comparable abc(b, "Hello"); //VALID. T maps to an unknown super type of Object (which can only be Object itself) //since String is already type-assignable to Object, it is of course guaranteed to be //type-assignable to any of Object super types. abc(c, "Hello"); Integer i1 = integers.get(0); Integer i2 = integers.get(1);
Not much to see abc implementation:
To initialize i1 , you will get a ClassCastException .
In my view, all these calls from the three methods have the following output, created because the actual type parameters are not used in the call to the static abc method.
method parameter <T> abc (List <T> a, T b>) inferred <Object> abc (List <Object>, Object)
This is totally wrong. It is not assumed that T Object in any of your examples, even in the case ? super Object ? super Object . T is allowed to capture a, and if you cannot assign String to this capture (as if it is a ? super Object ), you will have a type error.
Edit # 1
Regarding your update (I replaced your shared array with List<T> , since shared arrays unnecessarily obscure the problem):
// Showing inference at work List<Integer> a = Arrays.asList(10, 20, 30); // (5) T is inferred to be ? extends Object Method signature: ppp(? extends Object, List<? extends Object>) Method call signature: ppp(String, List<Integer>); ppp("Hello", a);
This is not true. The important mistake you make is here:
Method signature: ppp(? extends Object, List<? extends Object>)
This is not exactly what the capture engine does or should transfer your call. He decides T how <? extends Object> <? extends Object> , but how is one specific capture of <? extends Object> <? extends Object> . Let me call him capture-1-of<? extends Object> capture-1-of<? extends Object> . So your method should be like this:
Method signature: ppp(capture-1-of<? extends Object>, List<capture-1-of<? extends Object>>)
This means that there is a binding between the two parameters ... they must be allowed for the same capture. In general, it is very difficult to tell the compiler that two things are the same capture. In fact, even this is not a valid ppp call (although they are clearly the same capture):
List<? extends Integer> myList; ppp(myList.get(0), myList);
One way we could invoke ppp is through a common intermediary:
public static <T> void pppCaller(List<T> items) { ppp(items.get(0), items); } pppCaller(myList);
Only reliable way which you could call ppp with a wildcard was to call it like this:
List<? extends Integer> myList = new ArrayList<Integer>(); ppp(null, myList);
This is because null is the only thing you can assign to anything. On the other hand, if you had this method:
private static <T> void qqq(T item1, T item2) {}
You can really call it like this:
List<? extends Integer> myList; qqq(myList.get(0), myList.get(1));
Because in this case, the output can generalize T to Object. Since List<? extends Integer> List<? extends Integer> not covariant with List<Object> , it cannot do the same for ppp() .
However , what most people do to get around this is to weaken their method signature. Instead, declare ppp as follows:
public static <T> ppp(T item, List<? super T> items) { }
This follows the rules that Sean put on his PECS post
If (your method) creates , use extends , if it consumes , use super ... p>
Edit # 2
Regarding your last change:
public static void main(String [] args) { List <Integer> a = new LinkedList<Integer>(); qqq("Hello", a); // (21) error } static <T> void qqq(T t1, List <T> t2) {}
Object not a valid output for T I think this is what you are missing, so I will say it clearly:
A List<Integer> NOT assigned to type List<Object>
Not at all. If so, you can do something similar, which clearly violates type safety:
List<Integer> myInts = new ArrayList<Integer>(); List<Object> myObjects = myInts;
So T cannot be inferred as Object , since you would have to assign List<Integer> variable of type List<Object> .