Java Generics: How does method inference work when wildcard is being used in the method parameters?

You've set up a bit of a straw man by creating a LinkedList It could be a LinkedList.

Up vote 7 down vote favorite share g+ share fb share tw.

Extends Object) Method call signature: ppp(String, Integer); ppp("Hello", a); // ok } static void abc(List a, T b) {} static void def(List a) {} static void ppp(T t1, T t2){} } To begin with, look at clause 5 showing inference at work. Now clause 5 section is a working section. If that is what it is, then why does clause (1) & (2) have errors?

From my view, all these 3 methods calling have the same inference generated since no actual type parameters is used on the abc method call. Method parameter abc (List a, T b>) inferred abc (List , Object) // (4) Please bear in mind, method abc() and def() is my method. Compiler doesn't know what I want to do with the List in this method.

I might just print the list size or might not even do anything at all as shown above. So there is no get or set involved here. CONTINUATION --> This is getting very confusing for me.

Class y { public static void main(String args) { List a = new LinkedList(); List be = new LinkedList(); ppp("Hello", new Integer(1)); // (20) ok qqq("Hello", a); // (21) error qqq("Hello", b); // (22) ok } static void ppp(T t1, T t2) {} static void qqq(T t1, List t2) {} } Note that clause 21 is the same as clause 20 except 2nd parameter is being made to be a List instead of Integer. Clause 20 is ok cos' T is inferred to be Object. Clause 22 is ok.

Same reason as clause 20. Clause 21 failed? T could also be inferred to be Object too - would work too?

Java generics link|improve this question edited Sep 26 '11 at 19:17 asked Sep 26 '11 at 15:18yapkm0156229 72% accept rate.

You've set up a bit of a straw man by creating a LinkedList in each case. That can make it difficult to see the problem. What you have to remember is that when the compiler gets to those method invocations, it doesn't know that you created a LinkedList.

It could be a LinkedList, for example. So let's look at your code with more interesting initializations: List integers = new LinkedList(); List a = integers; List be = integers; List c = new LinkedList(); //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's super types. Abc(c, "Hello"); Integer i1 = integers. Get(0); Integer i2 = integers.

Get(1); It doesn't take much to see that if the implementation of abc was this: //a perfectly valid implementation static void abc(List a, T b) { a. Add(b); } That you would get a ClassCastException when initializing i1. From my view, all these 3 methods calling has the following inference generated since no actual type parameters is used on the abc static method call.

Method parameter abc (List a, T b>) inferred abc (List , Object) // (4) This is categorically wrong. It is not inferred that T is Object in any of your examples, not even in the case of? Super Object.

T is resolved to the capture of a, and unless you can assign a String to that capture (as is the case when it's? Super Object) you will have a type error. Edit #1 Regarding your update (I've replaced your generic array with a List since generic arrays needlessly cloud the issue): // Showing inference at work List a = Arrays.

AsList(10, 20, 30); // (5) T is inferred to be? Extends Object Method signature: ppp(? extends Object, List) Method call signature: ppp(String, List); ppp("Hello", a); // ok This is not correct. The crucial mistake you're making is here: Method signature: ppp(? extends Object, List) This is not at all what the capture engine does or should translate your invocation into.

It resolves T as but as one specific capture of . Let's call it capture-1-of. Thus your method must be like this: Method signature: ppp(capture-1-of, List>) This means that there is a binding between the two parameters...they must resolve to 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 invocation of ppp (even though they are clearly the same capture): List myList; ppp(myList. Get(0), myList); One way we could invoke ppp is through a generic intermediary: public static void pppCaller(List items) { ppp(items.

Get(0), items); } pppCaller(myList); The only sure-fire way you could invoke ppp with a wildcarded list would be to invoke it like this: List myList = new ArrayList(); ppp(null, myList); That's because the null is the only thing that you can assign to anything. On the other hand, if you had this method: private static void qqq(T item1, T item2) {} You could indeed invoke it like this: List myList; qqq(myList. Get(0), myList.

Get(1)); Because in this case, the inference can generalize T to Object. Since List is not covariant with List, it cannot do the same for ppp(). However, what most people do to get around this is to relax their method signature.

Instead, declare ppp as the following: public static ppp(T item, List items) { } This follows the guidelines that Sean put in his post of "PECS" If (your method) produces, use extends, if it consumes, use super. Edit #2 Regarding your latest edit: public static void main(String args) { List a = new LinkedList(); qqq("Hello", a); // (21) error } static void qqq(T t1, List t2) {} Object is not a valid inference for T. I think this is something fundamental you're missing, so I'll say it clear: A List is NOT type-assignable to List Not at all.

If it were, you could do something like this which obviously violates type safety: List myInts = new ArrayList(); List myObjects = myInts; //doesn't compile! MyObjects. Add("someString"); Integer firstInt = myInts.

Get(0); //ClassCastException! So T cannot be inferred as Object, since it would require assigning a List to a variable of type List.

So the first 2 case failed due to inference. It cannot inferred an known wildcard and a string. Case 3 works since that lower bound wildcard is .. well Object.

Object and string .. well common superclass would be Object. In conclusion can we say that inference needs definite some sort of type to work? – yapkm01 Sep 26 '11 at 17:16 @yapkm01: I'm not sure what you mean by "failed due to inference.

" They failed because they wouldn't be type safe, and there is no alternate inference that could have been made that would make it type safe. In all three cases T is capturing the wildcard, the difference is that in case 3 it has a definite lower bound and thus you can assign something to a T. On the other hand, if the result was a T and you were assigning that to a String, then the upper bound is relevant.

If you don't have a lower or upper bound, than you are severely limited in what you can do. – Mark Peters 01 Sep7 at 17:20 Please see my update. It shows inference at work.

If you look at this concept, the same concept could be applied to clause 1-3. So I don't really see the actual problem unless inference requires a known type for one of its parameter in order for it work. The way I look at this now, this problem above has nothing to do with type safety (tho' yes generic is all about type safety using restriction).

But in this particular case, it cannot do inference because one of it's parameter is unknown (tho' like I said Object could be used as the final straw) – yapkm01 Sep 26 '11 at 18:38 You can refer to page 701 on 'A Programmers guide to JAVA SCJP by Mughal 3rd edition' – yapkm01 Sep 26 '11 at 18:47 I'm so confused. Maybe need to take some time to swallow this. LOL – yapkm01 Sep 26 '11 at 18:54.

Extends Foo does not mean "anything that extends Foo", but instead it means "some specific type that extends Foo". And since you are outside that definition, you have no way to know which specific sub-type of Foo it is. Update: As I said, it's complicated.

Here are some comments on your code. // a list of some specific type, and you don't know what type that is. // it's a sub-type ob Object, yes, which means that you can do // Object foo = a.

Get(0); , but the compiler has no way of knowing // whether it's a String so you can't pass in a String List a = new LinkedList(); // same here. '? ' and '?

Extends Object' are equivalent List be = new LinkedList(); // this is a list of Objects and superclasses thereof. // since there are no superclasses of Object, this is equivalent to // List. And through inheritance, a String is an Object, so // you can pass it in.

List c = new LinkedList(); Update 2: The problem here is that you are dealing with fixed, but unresolveable variables. // you can pass in a List and a String, // but if you pass in a List, the compiler has no idea what // '? ' is and just can't substitute 'String'.

// 'T' doesn't help you here, because 'T' can't match both // '? ' and 'String'. Static void abc(List a, T b) {} // this works because 'T' substitutes '?

' and doesn't have to worry // about a 2nd parameter static void def(List a) {} Read this question, it might shed some light on the problem: Java Generics: What is PECS?

1 That's right. But irregardless, that unknown subtype which extends Foo will always be a subtype of a cosmic supertype which is Object. – yapkm01 Sep 26 '11 at 15:23 @yapkm01 well yes, but potentially a different type from the one you are passing in (String) – Sean Patrick Floyd Sep 26 '11 at 15:25 My point is, when 2 parameters being used is totally different from one another (say number and string), the ultimate inference climbing back upwards is always Object.

The way I look at it is as long as you don't supply an actual type parameter, using inference irregardless of any parameter types (which could be different from one another), it always goes back to Object. Object is the main holder for everything. So it should work right?

– yapkm01 Sep 26 '11 at 15:28 @yapkm01 see my update – Sean Patrick Floyd Sep 26 '11 at 15:42 I understand what you are saying. Look at my update above on a new method called def. Notice there is only 1 parameter.

Since it only has 1 parameter, it doesn't need to do inference looking for a common superclass (needed for 2 or more parameters). Notice that? Extends Object works now (clause (4)).

This error that this question is about I believe has to do with inference to find a common superclass. When it couldn't find, it generates an error. Now the issue for me is, Object is always a common superclass for any type.

So this should work. – yapkm01 Sep 26 '11 at 15:54.

A wildcard would then needed to induce subtype covariance I'd rather say "try to simulate" since even after using wild-cards you can't get the same functionality you get for arrays. Then the question is why clause (3) works and not clause(2) or (1)? Consider the first declaration: List a = new LinkedList(); This declaration effectively says, I really don't know (or care) what kind of element the collection a contains.

This effectively shuts you off from "mutating" the collection since you might end up adding elements of type which are not really compatible with a. You can have List a = new ArrayList() but you still won't be able to put anything in it. Basically, in case an add is allowed, the compiler can't guarantee the type safety of the collection.

List be = new LinkedList(); Here you say be is a collection which contains elements which extend an Object. What kind of element, you don't know. This again as per the previous discussion doesn't allow you to add anything since you could end up compromising type safety.

List c = new LinkedList(); Here you say, c is a collection which contains elements of type Object and it's super-classes or in other words, at least an Object. Since each reference type in Java is assignment compatible with Object, it works in the third case.

List a means that a holds a specific type, which is unknown. Consider this more complete example: List floats = new ArrayList(); List a = str; /* Alias "str" as "a" */ abc(a, "Hello"); /* This won't compile... */ float f = floats. Get(0); /* .. if it did, you'd get a ClassCastException */ static abc(List a, T b) { a.

Add(b); /* Absolutely no problem here. */ } List means essentially the same thing as List, and would cause the same error. List means that the list holds a specific, but unknown super-type of Object, and the parameterized method can accept any object that is-a Object for the second parameter.

While the method invocation is type-safe, attempting to assign an unsafe value to c will cause an error: List numbers = new ArrayList(); List a = numbers; /* This won't compile... */ abc(a, "Hello"); Number n = numbers. Get(0); /* ...if it did, you'd get a ClassCastException.

I understand everything above but here's where the issue is. Method abc is doing nothing. How does the compiler knows I want to add?

Maybe I just want to do a get. You see, in a collection this restriction is done on the collection method itself like list.add() but this particular method abc() well it's mine. I could write any code inside or even might leave it blank as you can see above.

This method abc() is not a collection method. It's my own. There's no gurantee in this method abc() I want to add any records to that list.

Maybe I want to do just a print or just to get the size(). – yapkm01 Sep 26 '11 at 16:10 @yapkm01: The compiler doesn't know what you are doing. That's why code which you can see logically is type-safe (like invoking a method that does nothing) won't compile.

You can see it's safe, but the compiler isn't analyzing what you are doing, it's just enforcing basic rules about type-safety. If you could do something unsafe, it won't compile. It doesn't matter whether you really are doing something unsafe.

– erickson Sep 26 '11 at 16:39.

Integer is a subtype of Object, however List is not a subtype of List. This is quite confusing; arrays are more primitive and should be avoided. If a method parameter type is T, it accepts all S where S is a subtype of T.

To do this with List, the type should be List. It accepts all List where S is a subtype of T.

I cant really gove you an answer,but what I can give you is a way to a solution, that is you have to find the anglde that you relate to or peaks your interest. A good paper is one that people get drawn into because it reaches them ln some way.As for me WW11 to me, I think of the holocaust and the effect it had on the survivors, their families and those who stood by and did nothing until it was too late.

Related Questions