ArrayList with generics declaration in Java - java

The following code gives me an error for the line l.add
List<? extends Number> l = new ArrayList<Integer>();
l.add(1);
and forces me to write it as l.add(1, null);
Why is it so?

With a wildcard on the variable l, the compiler doesn't know (or care) which subclass of Number (or Number itself) the type parameter really is. It could be an ArrayList<Double> or an ArrayList<BigInteger>. It can't guarantee the type safety of what's passed in to add, and because of type erasure, the JVM can't catch a type mismatch either. So the compiler preserves type safety by disallowing such calls to add unless the value is null, which can be any type.
To get add to compile, you must declare l as:
List<? super Integer> l = new ArrayList<Integer>();
or you can remove the wildcard:
List<Integer> l = new ArrayList<Integer>();

It has to be:
List<? super Integer> l = new ArrayList<Integer>();
l.add(1);
Note the <? super Integer> declaration. It's called an upper-bound wildcard.
What does it do?
It restricts the Runtime type of the elements of the ArrayList to be one of the super classes of Integer, e.g. Integer, Number or Object, which means that you will be able to assign l to:
new ArrayList<Integer>
new ArrayList<Number>
new ArrayList<Object>
In the three cases, the statement l.add(1) is perfectly valid, so there's no compile-time error.
More info:
Upper-bounded wildcards in Java
What is PESC?

List<? extends Number> is different from List<T extends Number>. For List<T extends Number>, the l.add(new Integer(1)) would work.
If you use ? extends Number then you can't refer to the type, but you can still use ((List<Integer>)list).add((int) s).
You can write:
List<? extends Number> l = new ArrayList<>();
((List<Integer>)l).add((int) 1);
instead.

Generics is all about "Type Safety and invariance".
There are 2 scenario associated with your question.
Holding reference type.
Making modification to contents of that reference.
Your first statement is about holding the reference.
List<? extends Number> l = new ArrayList<Integer>();
Here, compiler ensures that l can hold the reference of Type List of objects which extends Number.
eg: List<Integer>, List<BigInteger>, List<Double> etc.
In our case it is List<Integer>. So far so good.
Now what am I allowed to add to l? Users could want to add anything (which extends Number, eg. Double, BigInteger or Integer). This would break the type safety. So to overcome this, the compiler ensures that the user is not allowed to add to such kind of references.
This kind of references are used mainly when we are only iterating through a type of list.
eg:
// Here numbers can take reference of List type of objects which extends Number.
public void printValue(List<? extends Number> numbers){
// Iterate through all the numbers.
for(Number number: numbers){
System.out.println(number.intValue());
}
}
Now coming to the 2nd scenario of this discussion, that is modifying the list.
For doing this, the compiler wants user to ensure type safety before making modification.
This can be done through <? super T>.
List<? super Number> n = new ArrayList<Number>();
Here the compiler allows to add subtypes of Number to n.
BigInteger b = new BigInteger("4");
Double d = new Double(2);
n.add(b); // valid
n.add(d); // valid
//Compiler won't allow here.
n.add(new CustomNumber()); // CustomNumber is not a subtype of Number. It addition in "n" is Invalid.

In simple words, List<? extends Number> declares a List of elements of unspecified type. You only specify that the type of the elements extends the class Number. l could be a List<Integer> or a List<Double>.
The compiler will allow to assign an element of the list to a Number, because it knows that anything in l is a Number or a subclass thereof.
But it will not allow to add any element to the list because the variable l could contain a List<Integer> that accepts only Integers, or a List<Double> that accepts only Doubles. No type is guaranteed to be accepted by the list. In some sense, List<? extends Number> is a read-only list.
If you need l to be a List of Numbers, maybe you pass it to a procedure that requires a List<Number>, then you must initialize it as an ArrayList<Number>().
But if you will always use Integers with that list, you should declare it as List<Integer> and initialize it with ArrayList<Integer>. Remember that Integer is a final class, it allows the compiler to optimize the code much more than with Numbers.

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) { ... }

How does the compiler differentiate <? extends Number> from <Number> when they both seemingly get compiled to the same thing after type erasure?

I'm trying to understand java generic covariancy, and I understand why
List<Number> list = new ArrayList<Integer>();
Integer = list.get(0);
is disallowed, so that a float isn't put into list and later cause issues. I believe type erasure would compile this down to:
List list = new ArrayList();
Integer = (Integer) list.get(0);
This is allowed:
List<? extends Number> list = new ArrayList<Integer>();
Integer = list.get(0);
Doesn't this compile down to the same thing after type erasure?
How does the wildcard help the compiler in enforcing that a float isn't put into the list when it holds Integers?
If you have List<? extends Number> list, you just cannot put anything into the resulting list. Your list is the "list containing some objects which extend Number", so calling add() with any argument is not compatible with this type. So this is how compiler controls whether you don't put floats: it does not allow to put anything.
Usually you change type to List<? extends Number> only at the places where you are not going to modify the list anymore and want just to read it.
Very well answered by Tagir and I would like to add few more points.
First of all the following will not even compile:
List<? extends Number> list = new List<Integer>();
I hope you know List is an interface. So now we have:
List<? extends Number> list = new ArrayList<Integer>();
Here list is a reference of type List<? extends Number> and it simply means it can refer to a list in which every element IS Number or more specifically every element extends class Number so it means all the following are permissible:
list = new ArrayList<Float>();
list = new ArrayList<Long>();
But we can not instantiate as a list of Object as Object is not Number.
list = new ArrayList<Object>(); // compile time error
Now when we take element out of this list we know only one thing for sure that it is going to be a Number and it means not sure but it can be Integeror Long as well. So we need casting as:
Integer integer = (Integer) list.get(0);
Float floatValue = (Float) list.get(0);
Number number = list.get(0); // no casting if assigning it to Number
This is mainly useful when we are producing the elements and not consuming them (check PECS). For example useful in printing the elements but not useful when you want to add any element in it as it will not even compile.
private static void printMe(List<? extends Number> numbers) {
for(Number number : numbers) {
System.out.println(number);
}
}
This can be invoked then as:
List<Integer> integerList = new ArrayList<>();
integerList.add(10);integerList.add(20);integerList.add(30);
printMe(integerList);
List<Long> longList = new ArrayList<>();
longList.add((long) 4.5);longList.add((long) 6.5);longList.add((long) 7.5);
printMe(longList);
This will simply print the elements in each list.

Is List<Double> a subtype of List<? extends Number> and why?

Here is what I know:
Double is a subtype of Number and List<Double> is not a subtype of List<Number>.
List<Dog> is not a subtype of List<Animal> because you can add Cat to List<Animal> but you can't do that with List<Dog>.
List<? extends Number> means this list can store variables of type Number and variables of subtype of Number. List<Double> means this list can store variables of type Double.
Please correct me if anything above is wrong and then Is List<Double> a subtype of List<? extends Number> and why?
All your items are correct.
Double is a subtype of Number and List<Double> is not a subtype of List<Number>.
List<Dog> is not a subtype of List<Animal> because you can add Cat to List<Animal> but you can't do that with List<Dog>.
That's correct. Generics aren't covariant (but arrays are!). Here's some follow up reading: Why are arrays covariant but generics are invariant?
List<? extends Number> means this list can store variables of type Number and variables of subtype of Number. List<Double> means this list can store variables of type Double.
This is true, but there's an important difference between List<Number> and List<? extends Number>. You can think of List<? extends Number> as a list of a specific Number-subtype (that is one of List<Double>, List<Integer>, List<Long>, ...) and a List<Number> as a list that can potentially contain a mix of Double, Integer, ...
As for your final question:
Is List<Double> a subtype of List<? extends Number>...
Yes, you can have for instance
List<Double> doubles = new ArrayList<>();
List<? extends Number> numbers = doubles;
... and why?
This is just the way subtyping is defined.
As for the motivation, suppose you have a method that accepts a list of numbers. If you let the parameter have the type List<Number> you won't be able to pass a List<Double> to it. (Your second item in your question explains why!) Instead, you can let the parameter have type List<? extends Number>. Since List<Double> is a subtype of List<? extends Number> it will work out.
There are a few points that should also be added
At runtime List<Double>, List<? extends Number>, List<?> and List<Object> are all identical. The generic parameter is not compiled at all. All the magic with generics is all compile time fun. This also means that if you have a List that is empty you have no idea what the generic parameter is!
Try not to think of the generic parameter as a "Subtype", the generic parameter really means "the class uses a generic parameter" so in this case "the list uses a Number". A good example of this is the HashMap source, if you have a look into the inner workings of it, it is actually storing an array of Entry, and the entries all have keys and values stored on them. When you look at more complex uses of generics you occasionally see this sort of use.
In the situation of a List the generic parameter means that the list stores that type of object, it could be that the object never stores an object of the type of the generic parameter at all! Like this:
public class DummyIterator<O> implements Iterator<O>{
public boolean hasNext() {
return false;
}
public O next() {
return null;
}
}
What does List<? extends Number> actually mean? Well it is pretty much the same as List<Number> for most uses. Keep in mind though that by saying ? you are pretty much saying 'I don't care about the type' which shows itself in this situation:
List<Double> doubles = new ArrayList<Double>();
List<? extends Number> numbers = doubles;
numbers.add(new Double(1)); //COMPILE ERROR
Number num = numbers.get(0);
So we can't add a Double to a <? extends Number>. But for this example:
List<Double> doubles = new ArrayList<Double>();
List<Number> numbers = doubles; //COMPILE ERROR
numbers.add(new Integer(1));
Number num = numbers.get(0);
You can't assign the List<Double> to a List<Number> which makes sense as you are specifically telling it, that lists use only Number types
So where should you use a ?? well really anywhere you could say "I don't care about the generic parameter" so for instance:
boolean equalListSizes(List<?> list1, List<?> list2) {
return list1.size() == list2.size();
}
You would use the ? extends Number type of format only where you are not modifying the object using the generic parameter. so for instance:
Number firstItem(List<? extends Number> list1) {
return list1.get(0);
}
Instead of using the ? and ? extends Number formats try using generics on the class / method instead, in most cases it makes your code more readable as well!:
<T extends Number> T firstItem(List<T> list1) {
return list1.get(0);
}
Class:
class Animal{}
class Dog extends Animal{}
class AnimalHouse<A extends Animal> {
List<A> animalsInside = new ArrayList<A>();
void enterHouse(A animal){
animalsInside.add(A);
}
A leaveHouse() {
return animalsInside.remove(0);
}
}
AnimalHouse<Dog> ah = new AnimalHouse<Dog>();
ah.enterHouse(new Dog());
Dog rufus = ah.leaveHouse();
As a bonus thought around generics you can also parameterise methods to return a particular class. A good example of this is the any() method in junit and the empty list collection:
Dog rufus = Matchers.<Dog>any();
List<Dog> dogs = Collections.<Dog>emptyList();
This syntax allows you to specify the return type of an object. Sometimes quite useful to know (makes some casting redundant)!
At runtime, List<T> and List<U> are identical to List (1).
However, this will change with the introduction of value types(expected to make it in the JDK 9 or JDK 10 release, not sooner than mid 2016). List<T> will not be the same as List<U> anymore due to numerous constraints explained here by Brian Goetz: http://cr.openjdk.java.net/~briangoetz/valhalla/specialization.html
(1) - T and U types are different in the previous statements
It helped me to see generics as constraints or contracts, not as types with subtypes.
So a variable List<? extends Number> var says: var is a list of some unknown type ?, which is constrained to be a subtype of Number.
List<Number> listN;
List<Double> listD;
List<? extends Number> listX;
...
Number n1 = ...;
Double d1 = ...;
...
listN.add(n1); // OK n1 is a Number
listN.add(d1); // OK d1 is a Double, which is a Number
listD.add(n1); // compile error, n1 is not a Double
listD.add(d1); // OK
listX.add(n1); // compile error, because the exact type of list is not known! (prevents putting a Dog in a Cat list)
listX.add(d1); // compile error, same cause
So when you can't even put a Number into a List<? extends Number>, whats the purpose of such a list? It allows you to work with lists of which the exact type does not matter for the task at hand:
// instead of several exactly typed methods...
int count(List<Number> numberList) {...}
int count(List<Object> objectList) {...}
// ...etc. you can have one with a degree of freedom:
int count(List<?> anyList) {...} // don't need to know the exact type of list
// instead of this...
Number sum(List<Number> numberList) {...}
Number sum(List<Double> doubleList) {...}
Number sum(List<Integer> integerList){...}
// you can do this, with a little less freedom in the ?
Number sum(List<? extends Number> list) {
// the only thing we need to know about the list's type is that it is some number type
...
Number ni = list.get(i);
...
}
Using wildcards ? extends X allows to relax rigid contracts to weaker conditions.
Using a named type parameter, you can establish constraints on allowed types between several variables:
// works for any type T of list, whatever T is
// T is the same in the argument and in the return
<T> T pickObject(List<T> list, int index) {
return list.get(index);
}
// works for any type T of list, if T is a Number type
// T is the same in the argument and in the return
<T extends Number> T pickNumber(List<T> list, int index) {
return list.get(index);
}
...
List<Number> list;
Number n = pickNumber(list);

why and what of polymorphic generic types

When should one use generic polymorphic types like these and what are its implications?
1. List<? super Dog> list = new ArrayList<Animal>();
2. List<? extends Animal> list = new ArrayList<Dog>();
3. List<?> list = new ArrayList<Dog>();
Would anyone use something like
List<? super Dog> list = new ArrayList<Dog>();
List<? extends Animal> list = new ArrayList<Animal>();
Note:
I understand when people use List<? super Dog> or List<? extends Animal> in method definitions. But what I don't understand is polymorphic generic typed object creation.
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?
Number[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, an 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 with binary compatibility with pre-existing code.
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 polution
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.
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 that 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 destiny.
public static void copy(List<? extends Number> source, List<? super Number> destiny) {
for(Number number : source) {
destiny.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);
They would use:
If they need a list of objects that are of a superclass of Dog. Like Animal, for instance.
If they need a list of objects that are animals (subclass of Animal). Like Dog, for instance.
If they need a list of objects, period. Which can incidentally be a list of dogs, for instance.

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