Issue with using 'extends' and 'super' in java generics with generic methods - java

There is a method :
public static <T> void addandDisp(Collection<T> cs, T t)
which is being called in the following way :
List<? extends Object> ls2 = new LinkedList<Number>();
addandDisp(ls2,new Object());
This gives a compile time error. On the other hand, if we had only one parameter, then the call is successful. Why is that ?
Moreover, this is successful :
List<? super String> ls1 = new LinkedList<String>();
addandDisp(ls1,new String());
while this is not :
List<? super String> ls1 = new LinkedList<Object>();
addandDisp(ls1,new Object());
What is the underlying logic ?

I would assume that addandDisp means add and display.
When you have a collection, defined as:
List<? extends Object> ls2 = new LinkedList<Number>();
This means that the compiler will allow you to assign the colletion to all the possible unknown subtypes of Object. Since you have the operation add, the compiler denies to give you green light, because it doesn't know if the provided object's type meets the restriction to be of the unknown subtype of Object. This is called covariance.
Similary, when you have a definition like this:
List<? super String> ls1 = new LinkedList<String>();
The compiler allows you to assing ls1 to:
LinkedList<String>(); //you can add Strings to the list
LinkedList<Object>(); //you can add Strings and Objects to the list
In this case, the compiler will be completely aware if the object you're trying to pass meets the condition to be subtype of the generic type of the collection. This is called contravariance.
More info:
What is PECS?

Here is another approach, but others gave a quite detailed response.
The first example:
List<? extends Object> ls0 = new LinkedList<Number>();
addandDisp(ls0, new Object());
Here ls0 may be of any type that is a subclass of Object, like a Number as your example shows.
Now what if this could work? It would mean that if you can put an Object into any of the collections.
Imagine I want to iterate your previous list of Number objects:
for (Number n : numbers) {
...
}
If I could put an Object here, then Plaff! I would get a class cast exception immediately.
The second example
List<? super String> ls1 = ...;
addandDisp(ls1,new String());
Let's play a bit with the object hierarchy: String is a subclass of CharSequence which is a subclass of Object.
You get ls1 here which is an unknown supertype of String (e.g., a CharSequence). Is it all right to put a String into a list of character sequences? Yup, it's okay, since they share the same interface for sure! You can handle them through it in the same way.
The third example
List<? super String> ls1 = ...;
addandDisp(ls1,new Object());
Let's suppose the previous situation: you assign ls1 a list of CharSequences. Can you add an object to a List<CharSequence>? Nope, for the same reason as the first example fails.
Hope that helps clarifying your issues a bit :-)

On the other hand, if we had only one parameter, then the call is successful. Why is that ?
Your method has a single type parameter. When you invoke a method, you either provide a type argument explicitly or have one be inferred implicitly.
When you invoke it like
addandDisp(ls2, new Object());
the compiler needs to extract a type to bind to T from both method arguments, since both method parameters rely on the method's type parameter, T.
The issue here is that ? extends Object and Object do not produce a single type that can be bound to T safely. Imagine you had
public static <T> void addandDisp(Collection<T> cs, T t) {
cs.add(t);
}
...
List<? extends Object> ls2 = new LinkedList<Number>();
addandDisp(ls2, new Object());
This add should not be allowed since an Object should not be used where a Number would have been expected. Type safety would break.
If you have a single parameter
public static <T> void addandDisp(Collection<T> cs) {
Then the compiler uses the single argument to infer the type argument. There is then no ambiguity about what to use.

1 example: Object is not ? extends Object (for example, Number is ? extends Object, but Object is not Number). Thus, it is not type safe.
2 example: String is ? super String. It is type safe.
3 example: Object is not ? super String. Not safe again.

Related

Java: Different Generic type declaration and instance

I see that you have to use a generic form both when declaring the variable and assigning the instance. The question I want to ask is this:
Is there any case the type of variable and instance can be different?
Like
List<Object> list = new ArrayList<Integer>();
First of all, you can use the diamond operator to infer the type on the right:
List<Object> list = new ArrayList<>();
Is there any case the type of variable and instance can be different?
Generic types in Java are erased at runtime. That means that the instance has no generic type. The type only exists at compile-time, on the variable.
The generic types on the left and the right do not have to be exactly the same, but then you have to use wild-cards:
List<?> list = new ArrayList<Integer>();
List<? extends Number> list = new ArrayList<Integer>();
With these wild-cards, you are then quite limited in what you can still do with the objects. For example, you can only get a Number out of a List<? extends Number>, but you cannot add a new Integer to it (because it might have been a List<Double> for all you know).
This is still useful if you want to write methods that can accept lists of some interface type:
boolean areAllPositive(List<? extends Number> numbers) { ... }

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

Supposedy i have the following:
class x {
public static void main(String [] args) {
List <?> a = new LinkedList<Object>();
List <? extends Object> b = new LinkedList<Object>();
List <? super Object> c = new LinkedList<Object>();
abc(a, "Hello"); // (1) Error
abc(b, "Hello"); // (2) Error
abc(c, "Hello"); // (3) ok
def(b); // (4) ok
// Showing inference at work
Integer[] a = {10, 20, 30}; // (5)
T is inferred to be ? extends Object
Method signature: ppp(? extends Object, ? extends Object[])
Method call signature: ppp(String, Integer[]);
ppp("Hello", a); // ok
}
static <T> void abc(List<T> a, T b) {}
static <T> void def(List<T> a) {}
static <T> 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 <T> abc (List <T> a, T b>)
inferred <Object> abc (List <Object>, 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 <Integer> a = new LinkedList<Integer>();
List <Object> b = new LinkedList<Object>();
ppp("Hello", new Integer(1)); // (20) ok
qqq("Hello", a); // (21) error
qqq("Hello", b); // (22) ok
}
static <T> void ppp(T t1, T t2) {}
static <T> void qqq(T t1, List <T> 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?
The hard thing about the wildcard is to realize ? 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<Object>();
// same here. '?' and '? extends Object' are equivalent
List <? extends Object> b = new LinkedList<Object>();
// this is a list of Objects and superclasses thereof.
// since there are no superclasses of Object, this is equivalent to
// List<Object>. And through inheritance, a String is an Object, so
// you can pass it in.
List <? super Object> c = new LinkedList<Object>();
Update 2:
The problem here is that you are dealing with fixed, but unresolveable variables.
// you can pass in a List<String> 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 <T> void abc(List<T> a, T b) {}
// this works because 'T' substitutes '?' and doesn't have to worry
// about a 2nd parameter
static <T> void def(List<T> a) {}
Read this question, it might shed some light on the problem:
What is PECS (Producer Extends Consumer Super)?
You've set up a bit of a straw man by creating a LinkedList<Object> 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<Object>. It could be a LinkedList<Integer>, for example.
So let's 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'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 <T> void abc(List<T> 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 <T> abc (List <T> a, T b>)
inferred <Object> abc (List <Object>, 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<T> since generic arrays needlessly cloud the issue):
// 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); // ok
This is not correct. The crucial mistake you're making is here:
Method signature: ppp(? extends Object, List<? extends Object>)
This is not at all what the capture engine does or should translate your invocation into. It resolves T as <? extends Object> but as one specific capture of <? extends Object>. Let's call it capture-1-of<? extends Object>. Thus your method must 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 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<? extends Integer> myList;
ppp(myList.get(0), myList);
One way we could invoke ppp is through a generic intermediary:
public static <T> void pppCaller(List<T> 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<? extends Integer> myList = new ArrayList<Integer>();
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 <T> void qqq(T item1, T item2) {}
You could indeed invoke it like this:
List<? extends Integer> myList;
qqq(myList.get(0), myList.get(1));
Because in this case, the inference can generalize T to Object. Since List<? extends Integer> is not covariant with List<Object>, 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 <T> ppp(T item, List<? super T> 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 <Integer> a = new LinkedList<Integer>();
qqq("Hello", a); // (21) error
}
static <T> void qqq(T t1, List <T> 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<Integer> is NOT type-assignable to List<Object>
Not at all. If it were, you could do something like this which obviously violates type safety:
List<Integer> myInts = new ArrayList<Integer>();
List<Object> 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<Integer> to a variable of type List<Object>.
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<Object>();
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<String>() 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 <? extends Object> b = new LinkedList<Object>();
Here you say b 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 <? super Object> c = new LinkedList<Object>();
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.
Integer[] is a subtype of Object[], however List<Integer> is not a subtype of List<Object>. 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<? extends T>. It accepts all List<S> where S is a subtype of T.
List<?> a means that a holds a specific type, which is unknown. Consider this more complete example:
List<Float> floats = new ArrayList<Float>();
List<?> a = floats; /* Alias "floats" as "a" */
abc(a, "Hello"); /* This won't compile... */
float f = floats.get(0); /* .. if it did, you'd get a ClassCastException */
static <T> abc(List<T> a, T b) {
a.add(b); /* Absolutely no problem here. */
}
List<? extends Object> means essentially the same thing as List<?>, and would cause the same error.
List<? super Object> 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<Number> numbers = new ArrayList<Number>();
List<? super Object> a = numbers; /* This won't compile... */
abc(a, "Hello");
Number n = numbers.get(0); /* ...if it did, you'd get a ClassCastException */

What are the risks of explicitly casting from a list of type List<? extends MyObject> to a list of type List<MyObject> in Java?

I think the title should explain it all but just in case...
I want to know what risks and potential issues relating to casting can arise from the following snippet of Java code:
List<? extends MyObject> wildcardList = someAPI.getList();
List<MyObject> typedList = (List<MyObject>) wildcardList;
My thoughts are that all objects in the wildcardList should be an instance of MyObject (exact type or subclass) and so whenever objects are retrieved from typedList then there should never be a ClassCastException. Is this correct? If so why does the compiler generate a warning?
There should be no problem as long as just you retrieve objects from the list. But it could result in runtime exception if you invoke some other methods on it like the following code demonstrate:
List<Integer> intList = new ArrayList<Integer>();
intList.add(2);
List<? extends Number> numList = intList;
List<Number> strictNumList = (List<Number>) numList;
strictNumList.add(3.5f);
int num = intList.get(1); //java.lang.ClassCastException: java.lang.Float cannot be cast to java.lang.Integer
You are correct about retrieving objects from typedList, this should work.
The problem is when you later add more objects to typedList. If, for instance, MyObject has two subclasses A and B, and wildcardList was of type A, then you can't add a B to it. But since typedList is of the parent type MyObject, this error will only be caught at runtime.
Consider you do something like:
List<ChildClassOne> childClassList = new ArrayList<ChildClassOne>();
childClassList.add(childClassOneInstanceOne);
childClassList.add(childClassOneInstanceTwo);
List<? extends MyObject> wildcardList = childClasslist; // works fine - imagine that you get this from a method that only returns List<ChildClassOne>
List<MyObject> typedList = (List<MyObject>) wildcardList; // warning
typedList.add(childClassTwoInstanceOne); // oops my childClassList now contains a childClassTwo instance
ChildClassOne a = childClassList.get(2); // ClassCastException - can't cast ChildClassTwo to ChildClassOne
This is the only major problem. But if you only read from your list it should be ok.
This is similar to
List<Object> obj = (List<Object>) new ArrayList<String>();
I hope the problem is evident. List of subtypes can't be cast to Lists of supertypes.
In addition to the answers already posted, take a look at the following
what-is-the-meaning-of-the-type-safety-warning-in-certain-java-generics-casts
confused-by-java-generics-requiring-a-cast
The compiler generates a warning incase an element from the type is accessed later in your code and that is not the generic type defined previously. Also the type information at runtime is not available.

Transfering a Set with a Wildcarded Generic to a List in Java

I have a data type that contains a set and a method that expects List<? extends MyClass>. The data type has Set<? extends MyClass>. I need to be able to move the stuff out of the set and into the List. The order it goes into the list doesn't matter, it just needs to start keeping track of it so that it can be reordered when displayed. Suffice to say that changing the Set into a List in the data type is out of the question here.
This seems pretty easy at first. Create a new method that takes a Set instead of a List, changes it into a list and then passes it on to the old method that just took a list. The problem comes in changing the set to a list.
public void setData(Set<? extends MyClass> data) {
List<? extends Myclass> newData = ArrayList< /* What goes here? */ >();
for(ConcordaEntityBean o : data) {
newData.add(o);
}
setData(newData);
}
Obviously, I can't instantiate an ArrayList with a wildcard, it chokes. I don't know the type at that point. Is there some way to pull the type out of data and pass it to ArrayList? Can I just instantiate it with MyClass? Is there some other way to do this?
You can't instantiate it with MyClass because newData is not necessarily of type List<MyClass>, and Java does not support covariance (ie, S extends T does not imply that ArrayList<S> is a subtype of ArrayList<T>).
What you can do is this:
List<MyClass> newData = new ArrayList<MyClass>();
Now, everything in the Set (which is guaranteed to be a subtype of MyClass) can be put into newData.
Generally speaking, wildcard types enforce boundary constraints. You know that Set<? extends MyClass> is a Set parameterized with some type, and that type is bounded from above by MyClass. So when you are dealing with the elements from that Set, you can do no better than to treat them as MyClass objects.
Does this work:
List<MyClass> newData = new ArrayList<MyClass>(data);
setData(newData);
?
Or even a one-liner:
setData(new ArrayList<MyClass>(data));
I know my grasp of writing generic methods is shaky, but it seems like this would work:
public <T extends MyClass> void setData(Set<T extends MyClass> data) {
setData(new ArrayList<T>(data));
}

What is a difference between <? super E> and <? extends E>?

What is the difference between <? super E> and <? extends E>?
For instance when you take a look at class java.util.concurrent.LinkedBlockingQueue there is the following signature for the constructor:
public LinkedBlockingQueue(Collection<? extends E> c)
and for one for the method:
public int drainTo(Collection<? super E> c)
The first (<? super E>) says that it's "some type which is an ancestor (superclass) of E"; the second (<? extends E>) says that it's "some type which is a subclass of E". (In both cases E itself is okay.)
So the constructor uses the ? extends E form so it guarantees that when it fetches values from the collection, they will all be E or some subclass (i.e. it's compatible). The drainTo method is trying to put values into the collection, so the collection has to have an element type of E or a superclass.
As an example, suppose you have a class hierarchy like this:
Parent extends Object
Child extends Parent
and a LinkedBlockingQueue<Parent>. You can construct this passing in a List<Child> which will copy all the elements safely, because every Child is a parent. You couldn't pass in a List<Object> because some elements might not be compatible with Parent.
Likewise you can drain that queue into a List<Object> because every Parent is an Object... but you couldn't drain it into a List<Child> because the List<Child> expects all its elements to be compatible with Child.
The reasons for this are based on how Java implements generics.
An Arrays Example
With arrays you can do this (arrays are covariant)
Integer[] myInts = {1,2,3,4};
Number[] myNumber = myInts;
But, what would happen if you try to do this?
myNumber[0] = 3.14; //attempt of heap pollution
This last line would compile just fine, but if you run this code, you could get an ArrayStoreException. Because you’re trying to put a double into an integer array (regardless of being accessed through a number reference).
This means that you can fool the compiler, but you cannot fool the runtime type system. And this is so because arrays are what we call reifiable types. This means that at runtime Java knows that this array was actually instantiated as an array of integers which simply happens to be accessed through a reference of type Number[].
So, as you can see, one thing is the actual type of the object, and another thing is the type of the reference that you use to access it, right?
The Problem with Java Generics
Now, the problem with Java generic types is that the type information is discarded by the compiler and it is not available at run time. This process is called type erasure. There are good reason for implementing generics like this in Java, but that's a long story, and it has to do, among other things, with binary compatibility with pre-existing code (see How we got the generics we have).
But the important point here is that since, at runtime there is no type information, there is no way to ensure that we are not committing heap pollution.
For instance,
List<Integer> myInts = new ArrayList<Integer>();
myInts.add(1);
myInts.add(2);
List<Number> myNums = myInts; //compiler error
myNums.add(3.14); //heap pollution
If the Java compiler does not stop you from doing this, the runtime type system cannot stop you either, because there is no way, at runtime, to determine that this list was supposed to be a list of integers only. The Java runtime would let you put whatever you want into this list, when it should only contain integers, because when it was created, it was declared as a list of integers.
As such, the designers of Java made sure that you cannot fool the compiler. If you cannot fool the compiler (as we can do with arrays) you cannot fool the runtime type system either.
As such, we say that generic types are non-reifiable.
Evidently, this would hamper polymorphism. Consider the following example:
static long sum(Number[] numbers) {
long summation = 0;
for(Number number : numbers) {
summation += number.longValue();
}
return summation;
}
Now you could use it like this:
Integer[] myInts = {1,2,3,4,5};
Long[] myLongs = {1L, 2L, 3L, 4L, 5L};
Double[] myDoubles = {1.0, 2.0, 3.0, 4.0, 5.0};
System.out.println(sum(myInts));
System.out.println(sum(myLongs));
System.out.println(sum(myDoubles));
But if you attempt to implement the same code with generic collections, you will not succeed:
static long sum(List<Number> numbers) {
long summation = 0;
for(Number number : numbers) {
summation += number.longValue();
}
return summation;
}
You would get compiler erros if you try to...
List<Integer> myInts = asList(1,2,3,4,5);
List<Long> myLongs = asList(1L, 2L, 3L, 4L, 5L);
List<Double> myDoubles = asList(1.0, 2.0, 3.0, 4.0, 5.0);
System.out.println(sum(myInts)); //compiler error
System.out.println(sum(myLongs)); //compiler error
System.out.println(sum(myDoubles)); //compiler error
The solution is to learn to use two powerful features of Java generics known as covariance and contravariance.
Covariance
With covariance you can read items from a structure, but you cannot write anything into it. All these are valid declarations.
List<? extends Number> myNums = new ArrayList<Integer>();
List<? extends Number> myNums = new ArrayList<Float>();
List<? extends Number> myNums = new ArrayList<Double>();
And you can read from myNums:
Number n = myNums.get(0);
Because you can be sure that whatever the actual list contains, it can be upcasted to a Number (after all anything that extends Number is a Number, right?)
However, you are not allowed to put anything into a covariant structure.
myNumst.add(45L); //compiler error
This would not be allowed, because Java cannot guarantee what is the actual type of the object in the generic structure. It can be anything that extends Number, but the compiler cannot be sure. So you can read, but not write.
Contravariance
With contravariance you can do the opposite. You can put things into a generic structure, but you cannot read out from it.
List<Object> myObjs = new List<Object>();
myObjs.add("Luke");
myObjs.add("Obi-wan");
List<? super Number> myNums = myObjs;
myNums.add(10);
myNums.add(3.14);
In this case, the actual nature of the object is a List of Objects, and through contravariance, you can put Numbers into it, basically because all numbers have Object as their common ancestor. As such, all Numbers are objects, and therefore this is valid.
However, you cannot safely read anything from this contravariant structure assuming that you will get a number.
Number myNum = myNums.get(0); //compiler-error
As you can see, if the compiler allowed you to write this line, you would get a ClassCastException at runtime (because item 0 in the list is an Object, not a Number).
Get/Put Principle
As such, use covariance when you only intend to take generic values out of a structure, use contravariance when you only intend to put generic values into a structure, and use the exact generic type when you intend to do both.
The best example I have is the following which copies any kind of numbers from one list into another list. It only gets items from the source, and it only puts items in the target.
public static void copy(List<? extends Number> source, List<? super Number> target) {
for(Number number : source) {
target.add(number);
}
}
Thanks to the powers of covariance and contravariance this works for a case like this:
List<Integer> myInts = asList(1,2,3,4);
List<Double> myDoubles = asList(3.14, 6.28);
List<Object> myObjs = new ArrayList<Object>();
copy(myInts, myObjs);
copy(myDoubles, myObjs);
<? extends E> defines E as the upper bound: "This can be cast to E".
<? super E> defines E as the lower bound: "E can be cast to this."
<? super E> means any object including E that is parent of E
<? extends E> means any object including E that is child of E .
I'm going to try and answer this. But to get a really good answer you should check Joshua Bloch's book Effective Java (2nd Edition). He describes the mnemonic PECS, which stands for "Producer Extends, Consumer Super".
The idea is that if you code is consuming the generic values from the object then you should use extends. but if you are producing new values for the generic type you should use super.
So for example:
public void pushAll(Iterable<? extends E> src) {
for (E e: src)
push(e);
}
And
public void popAll(Collection<? super E> dst) {
while (!isEmpty())
dst.add(pop())
}
But really you should check out this book:
http://java.sun.com/docs/books/effective/
You might want to google for the terms contravariance (<? super E>) and covariance (<? extends E>). I found that the most useful thing when comprehending generics was for me to understand the method signature of Collection.addAll:
public interface Collection<T> {
public boolean addAll(Collection<? extends T> c);
}
Just as you'd want to be able to add a String to a List<Object>:
List<Object> lo = ...
lo.add("Hello")
You should also be able to add a List<String> (or any collection of Strings) via the addAll method:
List<String> ls = ...
lo.addAll(ls)
However you should realize that a List<Object> and a List<String> are not equivalent and nor is the latter a subclass of the former. What is needed is the concept of a covariant type parameter - i.e. the <? extends T> bit.
Once you have this, it's simple to think of scenarios where you want contravariance also (check the Comparable interface).
Before the answer; Please be clear that
Generics only compile time feature to ensure TYPE_SAFETY, it wont b available during RUNTIME.
Only a reference with Generics will force the type safety; if the reference don't declared with generics then it will work without type safty.
Example:
List stringList = new ArrayList<String>();
stringList.add(new Integer(10)); // will be successful.
Hope this will help you to understand wildcard more clear.
//NOTE CE - Compilation Error
// 4 - For
class A {}
class B extends A {}
public class Test {
public static void main(String args[]) {
A aObj = new A();
B bObj = new B();
//We can add object of same type (A) or its subType is legal
List<A> list_A = new ArrayList<A>();
list_A.add(aObj);
list_A.add(bObj); // A aObj = new B(); //Valid
//list_A.add(new String()); Compilation error (CE);
//can't add other type A aObj != new String();
//We can add object of same type (B) or its subType is legal
List<B> list_B = new ArrayList<B>();
//list_B.add(aObj); CE; can't add super type obj to subclass reference
//Above is wrong similar like B bObj = new A(); which is wrong
list_B.add(bObj);
//Wild card (?) must only come for the reference (left side)
//Both the below are wrong;
//List<? super A> wildCard_Wrongly_Used = new ArrayList<? super A>();
//List<? extends A> wildCard_Wrongly_Used = new ArrayList<? extends A>();
//Both <? extends A>; and <? super A> reference will accept = new ArrayList<A>
List<? super A> list_4__A_AND_SuperClass_A = new ArrayList<A>();
list_4__A_AND_SuperClass_A = new ArrayList<Object>();
//list_4_A_AND_SuperClass_A = new ArrayList<B>(); CE B is SubClass of A
//list_4_A_AND_SuperClass_A = new ArrayList<String>(); CE String is not super of A
List<? extends A> list_4__A_AND_SubClass_A = new ArrayList<A>();
list_4__A_AND_SubClass_A = new ArrayList<B>();
//list_4__A_AND_SubClass_A = new ArrayList<Object>(); CE Object is SuperClass of A
//CE; super reference, only accepts list of A or its super classes.
//List<? super A> list_4__A_AND_SuperClass_A = new ArrayList<String>();
//CE; extends reference, only accepts list of A or its sub classes.
//List<? extends A> list_4__A_AND_SubClass_A = new ArrayList<Object>();
//With super keyword we can use the same reference to add objects
//Any sub class object can be assigned to super class reference (A)
list_4__A_AND_SuperClass_A.add(aObj);
list_4__A_AND_SuperClass_A.add(bObj); // A aObj = new B();
//list_4__A_AND_SuperClass_A.add(new Object()); // A aObj != new Object();
//list_4__A_AND_SuperClass_A.add(new String()); CE can't add other type
//We can't put anything into "? extends" structure.
//list_4__A_AND_SubClass_A.add(aObj); compilation error
//list_4__A_AND_SubClass_A.add(bObj); compilation error
//list_4__A_AND_SubClass_A.add(""); compilation error
//The Reason is below
//List<Apple> apples = new ArrayList<Apple>();
//List<? extends Fruit> fruits = apples;
//fruits.add(new Strawberry()); THIS IS WORNG :)
//Use the ? extends wildcard if you need to retrieve object from a data structure.
//Use the ? super wildcard if you need to put objects in a data structure.
//If you need to do both things, don't use any wildcard.
//Another Solution
//We need a strong reference(without wild card) to add objects
list_A = (ArrayList<A>) list_4__A_AND_SubClass_A;
list_A.add(aObj);
list_A.add(bObj);
list_B = (List<B>) list_4__A_AND_SubClass_A;
//list_B.add(aObj); compilation error
list_B.add(bObj);
private Map<Class<? extends Animal>, List<? extends Animal>> animalListMap;
public void registerAnimal(Class<? extends Animal> animalClass, Animal animalObject) {
if (animalListMap.containsKey(animalClass)) {
//Append to the existing List
/* The ? extends Animal is a wildcard bounded by the Animal class. So animalListMap.get(animalObject);
could return a List<Donkey>, List<Mouse>, List<Pikachu>, assuming Donkey, Mouse, and Pikachu were all sub classes of Animal.
However, with the wildcard, you are telling the compiler that you don't care what the actual type is as long as it is a sub type of Animal.
*/
//List<? extends Animal> animalList = animalListMap.get(animalObject);
//animalList.add(animalObject); //Compilation Error because of List<? extends Animal>
List<Animal> animalList = animalListMap.get(animalClass);
animalList.add(animalObject);
}
}
}
}
A wildcard with an upper bound looks like " ? extends Type " and stands for the family of all types that are subtypes of Type , type Type being included. Type is called the upper bound .
A wildcard with a lower bound looks like " ? super Type " and stands for the family of all types that are supertypes of Type , type Type being included. Type is called the lower bound .
You have a Parent class and a Child class inherited from Parent class.The Parent Class is inherited from another class called GrandParent Class.So Order of inheritence is GrandParent > Parent > Child.
Now,
< ? extends Parent > - This accepts Parent class or either Child class
< ? super Parent > - This accepts Parent class or either GrandParent class
super: List<? super T> 'super' guarantees object to be ADDED to the collection is of type T.
extends: List<? extends T> 'extend' guarantees object READ from collection is of type T.
Explanation:
There are three things that need to be considered while understanding a difference between 'super' and 'extends' from type safety point of view.
1.Assigning : What type of collection can be assigned to the generic reference.
2.Adding : What type can be added to the collection referred.
3.Reading : What type can be read from collection referred
'<? super T>' ensures-
Any collection of type T or its superclass can be assigned.
Any object of type T or its subclass can be added to collection, as it
will always pass a 'Is A' test for T.
Type of the item read from the collection can not be guarantied except from being of type 'Object'. It can be anything of type T or its superclass which includes type 'Object'.
'<? extends T>' ensures-
Any collection of type T or its subclass can be assigned.
No object can be added as we can not determine type of reference.
(Even object of type 'T' can not be added, because generic reference might be assigned to the collection of subtype of 'T')
Item read from the collection can be guarantied to be of type 'T'.
Consider class hierarchy
class Base {}
class Intermediate extends Base{}
class ThirdLayer extends Intermediate{}
public void testGenerics() {
/**
* super: List<? super T> super guarantees object to be ADDED to the collection
* if of type T.
*
* extends: List<? extends T> extend guarantees object READ from collection is
* of type T
*
* Super:-
*
* Assigning : You can assign collection of Type T or its super classes
* including 'Object' class.
*
* Adding: You can add objects of anything of Type T or of its subclasses, as we
* are sure that the object of type T of its subclass always passes Is A test
* for T. You can NOT add any object of superclass of T.
*
* Reading: Always returns Object
*/
/**
* To a Collection of superclass of Intermediate we can assign Collection of
* element of intermediate or its Parent Class including Object class.
*/
List<? super Intermediate> lst = new ArrayList<Base>();
lst = new ArrayList<Intermediate>();
lst = new ArrayList<Object>();
//Can not assign Collection of subtype
lst = new ArrayList<ThirdLayer>(); //Error!
/**
* Elements of subtype of 'Intemediate' can be added as assigned collection is
* guaranteed to be of type 'Intermediate
*/
lst.add(new ThirdLayer());
lst.add(new Intermediate());
// Can not add any object of superclass of Intermediate
lst.add(new Base()); //Error!
Object o = lst.get(0);
// Element fetched from collection can not inferred to be of type anything
// but 'Object'.
Intermediate thr = lst.get(0); //Error!
/**
* extends: List<? extends T> extend guarantees object read from collection is
* of type T
* Assigning : You can assign collection of Type T or its subclasses.
*
* Adding: You cannot add objects of anything of Type T or even objects of its
* subclasses. This is because we can not be sure about the type of collection
* assigned to the reference.
*
* Reading: Always returns object of type 'T'
*/
// Can assign collection of class Intermediate or its subclasses.
List<? extends Intermediate> lst1 = new ArrayList<ThirdLayer>();
lst1 = new ArrayList<Base>(); //Error! can not assign super class collection
/**
* No element can be added to the collection as we can not be sure of
* type of the collection. It can be collection of Class 'Intermediate'
* or collection of its subtype. For example if a reference happens to be
* holding a list of class ThirdLayer, it should not be allowed to add an
* element of type Intermediate. Hence no addition is allowed including type
* 'Intermediate'.
*/
lst1.add(new Base()); //Error!
lst1.add(new ThirdLayer()); //Error!
lst1.add(new Intermediate()); //Error!
/**
* Return type is always guaranteed to be of type 'Intermediate'. Even if the
* collection hold by the reference is of subtype like 'ThirdLayer', it always
* passes the 'IS A' test for 'Intermediate'
*/
Intermediate elm = lst1.get(0);
/**
* If you want a Collection to accept (aka to be allowed to add) elements of
* type T or its subclasses; simply declare a reference of type T i.e. List<T>
* myList;
*/
List<Intermediate> lst3 = new ArrayList<Intermediate>();
lst3 = new ArrayList<ThirdLayer>(); //Error!
lst3 = new ArrayList<Base>(); //Error!
lst3.add(new Intermediate());
lst3.add(new ThirdLayer()); // Allowed as ThirdLayer passes 'IS A' for Intermediate
lst3.add(new Base()); //Error! No guarantee for superclasses of Intermediate
}

Categories

Resources