Java. Wildcards in Collections - java

Java. Wildcards in Collections
I’m having a lot of trouble understanding wildcards in Collections, even after reading similar posts in Stack Overflow and various tutorials sites. I've made a very simple example below. Can you explain how I would choose between Collection< myClass >, Collection< ? Extends MyClass >, and Collection< ? Super MyClass >?
package z5;
import java.util.ArrayList;
public class Z5 {
public static class Animal{
}
public static class Mammal extends Animal{
}
public static class Reptile extends Animal{
}
public static class Lion extends Mammal{
}
public static class Tiger extends Mammal{
}
public static class Snake extends Reptile{
}
public static void main(String[] args) {
ArrayList<Mammal> catHouse1 = new ArrayList<Mammal>();
catHouse1.add(new Lion());
catHouse1.add(new Tiger());
catHouse1.add(new Mammal());
catHouse1.add(new Animal()); //ERROR
ArrayList<? super Mammal> catHouse2 = new ArrayList<Mammal>();
catHouse2.add(new Lion());
catHouse2.add(new Tiger());
catHouse2.add(new Mammal());
catHouse2.add(new Animal()); //ERROR
ArrayList<? extends Mammal> catHouse3 = new ArrayList<Mammal>();
catHouse3.add(new Lion()); //ERROR
catHouse3.add(new Tiger()); //ERROR
catHouse3.add(new Mammal()); //ERROR
catHouse3.add(new Animal()); //ERROR
ArrayList<Mammal> zooMammals = new ArrayList<Mammal>();
zooMammals.addAll(catHouse1);
zooMammals.addAll(catHouse2); //ERROR
zooMammals.addAll(catHouse3);
ArrayList<Animal> zooAnimals = new ArrayList<Animal>();
zooAnimals.addAll(catHouse1);
zooAnimals.addAll(catHouse2); //ERROR
zooAnimals.addAll(catHouse3);
}
}
In the example above, I make a hierarchy of classes. Animal is the superclass of Mammal and Reptile, and Mammal is the superclass of Lion and Tiger.
I make ArrayLists for three Cat Houses in my zoo. The first is simply ArrayList< Mammal >. I can add any Object of type Mammal or its subclasses.
The second is ArrayList< ? super Mammal>. I can also add any Object of type Mammal or its subclasses.
The third is ArrayList< ? extends Mammal>. I can't add anything to it.
Finally, I add my three cat houses into zoo Collections. Animal and Mammal are the main superclasses here, and the behavior is the same regardless of which type the recipient ArrayList holds.
ArrayList < Mammal > and ArrayList can be added to the zoos.
ArrayList cannot.
Here are my questions:
1) If I want to make an array that holds all subclasses of a certain superclass, why would I need a wildcard? Couldn't I just declare everything ArrayList< Superclass > and get the functionality I need?
2) I understand that "< ? extends superclass >" accepts the superclass and all its subclasses. Ergo, < ? extends Mammal >" accepts Mammals, Lions, and Tigers. That sounds exactly like "< Mammal >". What's the difference?
3) I read that "< ? super className >" accepts any class that's a superclass of className. That doesn't sound right. In the example above, Lions are not superclasses of Mammals, but "< ? super Mammal >" accepts Lions. Animals are superclasses of Mammals, but "< ? super Mammal >" doesn't accept it. I think I have wrong information there.
4) If "< ? extends superclass >" is read-only, how dod I populate it to begin with? And what's the point of having an empty list that you can only read from?
5) Why does the addAll method not work on "< ? super className >"?
I know these are fundamental questions, and I understand they've been answered before. I'm trying to give a code example that's as simple as possible and hopefully get me an answer that's as clear as possible. Thanks in advance for any advice you can offer.

I won't go into great details.
List<Animal> means: this list contains instances of Animal. So you can add any Animal you want, and you're sure to get an Animal when getting elements from this list.
List<? extends Animal> means: this list is a generic list, but we don't know the type of the generic type. All we know about it is that the generic type is Animal, or any subclass of Animal. So it could be a List<Animal>, a List<Reptile> or a List<Lion>, for example. But we don't know. So, when getting an element from this list, you're guaranteed to get an Animal. But you won't be allowed to add anything into the list, because you don't know its type. If you were allowed to add something, you could store a Lion in a List<Reptile>, and the list wouldn't be type-safe anymore.
List<? super Animal> means: this list is a generic list, but we don't know the type of the generic type. All we know about it is that the generic type is Animal, or any superclass or super interface of Animal. So it could be a List<Animal> or a List<Object>, for example. So, when getting an element from this list, all you can say for sure is that it's an Object. What you can do for sure is to add any Animal you want in this list though, because both List<Animal> and List<Object> accept Animal, Lion, Reptile or whatever subclass of Animal.
Most of the time, you use these wildcards for arguments of your methods.
When a method takes a list as argument and is only interested in reading from the list (i.e. the list is a producer), you'll use List<? extends Animal>. This makes your method more reusable, because it can be used with a List<Animal>, but also with a List<Reptile>, a List<Lion>, etc.
When a method takes a list as argument and is only interested in adding elements to the list (i.e. the list is a consumer), you'll use List<? super Animal>. This makes your method more reusable, because it can be used with a List<Animal>, but also with a List<Object>.
This rule is known as PECS: Procucer: Extends; Consumer: Super.
If your method takes a list as argument and must both get and add elements to/from the list, you'll use a List<Animal>.

If you just want a list that can contain Animal or subclasses Animal you should use List<Animal>. I think this is what you need most of the time.
extends:
ArrayList<? extends Mammal> list = ..
This means that list is of type Mammal or one of its sub classes. So you can do this:
ArrayList<? extends Mammal> list = new ArrayList<Tiger>(); // List of type Tiger
When using <? extends Mammal> you can't insert anything into list (except null). The reason that there is no typesafe way to insert anything. You can't add a Mammal to list because list might be of type Tiger (like in the example above). A Mammal is no Tiger so this can't work.
You can't add a Tiger to list either. That's because <? extends Mammal> doesn't tell you anything about Tiger. It would be also possible that list is of type Lion.
super:
With <? super Mammal> it is the opposite situation. <? super Mammal> means that the list is of type Mammal or one of its superclasses. You can do this:
ArrayList<? super Mammal> list = new ArrayList<Animal>(); // List of type Animal
You can add any subclass of Mammal to this list. This is safe because subclasses of Mammal are always subclasses of Animal. However you can't get anything out of the list with another type than Object. Thats because you don't know the exact list type.
Mammal m = list.get(0); // can't work because list can be a List<Animal>

Related

Java list generics with multiple bounds

I'm struggling to understand why the following code doesn't work :
public <E extends Animal & IQuadruped> void testFunction()
{
List<E> list = new ArrayList<E>();
Dog var = new Dog();
list.add(var);
}
with Dog being the following :
public class Dog extends Animal implements IQuadruped
{
}
And I get a compile error on the add :
The method add(E) in the type List<E> is not applicable for the arguments (Dog)
I just want to make sure my list elements extend/implement both classes, and Dog fullfil those conditions, why is it not working ?
Thank you
What <E extends Animal & IQuadruped> means is "a particular type that is a subtype of both Animal and IQuadruped", and not "any type that is a subtype of both Animal and IQuadruped"
The reason it's difficult to grasp the difference is that in everyday thinking we don't make that distinction explicit. If for example you agree to go out for lunch with someone in a restaurant, and they say "any day next week is good for me", you automatically know that means you need to turn up on the same day. And not that you can go at any time and they'll be there.
In this case there's no guarantee that the E the caller chooses will definitely be Dog, so your code won't work.
The obvious wrong solution is to specify<E extends Dog>, because it would guarantee that E is a subtype of Dog. However this is wrong for the exact same reason, E could be any subtype of Dog, let's say E is a Poodle, so when you create a List<Poodle>, you won't be able to put your new Dog() in there, because it isn't a Poodle.
The correct bound is <E super Dog>, because that means E is definitely a type that you can cast a Dog instance to. It could be Dog itself, it could be Animal or IQuadruped or even Object. But it guarantees that you can put a Dog in the list.
The name of this principle is PECS: producer extends, consumer super, and you can read about it here.
Generics type erasure is what you are getting. By the time the JVM executes the line where you are adding to the list, the generics is already lost. To get this to work, you would have to do something like:
public <E extends Animal & IQuadruped> void testFunction(E param) {
List<E> list = new ArrayList<E>();
list.add(param);
}
public void addDog() {
testFunction(new Dog())
}

Understanding wildcard-constraint <? super T>

I can work with basic generic expression but the wildcard-constraints just mess with my mind.
Info: Student extends Person and Person extends Animal
List<? super Animal> aList= new ArrayList<>();
// does not compile as expected,
// since the list is restricted to something that has Animal as superclass.
// aList.add(new Object());
aList.add(new Animal()); // I can add animal
aList.add(new Person()); // I can add Person
aList.add(new Student()); // I can add Student
Animal a = new Animal();
Animal b = new Animal();
Person p = new Person();
Student s = new Student();
// test
a = b; //I can assign an animal to another animal
a = p; // I can assign a person to an animal
a = s; // I can assign a student to an animal
Animal animal = aList.get(0); // DOES NOT COMPILE, WHY ?
QUESTION: I don't understand why the last assignment does not work. The examples above show, that anything in that list is definitely an animal, yet I can't get an animal from that list.
MORE SPECIFIC: When I know that I can add only types which have Animal as superclass, why can't I expect to remove objects from type Animal ?
FACT: I can ONLY add objects which extend Animal! I just tried to add a car object and it does not work !
I am starting to doubt my sanity, I hope you can help me. Thank You
Additionally:
Object animal = aList.get(0); // works
Why does this statement work even though I know that I can't add an Object-Type ?
SOLUTION: (Based on the accepted answer)
I misunderstood the meaning of <? super Animal>
What I thought it means: Any class which has Animal as superclass.
What it (apparently) means: Any Class which is superclass of Animal.
Therefore a List might also contain objects of type Object which is why Animal animal = aList.get(0); fails.
Cheers
The other answers here are right, but not stated clearly enough, which is where the confusion is coming from.
A List<? extends Animal> does not mean "A list of things that all extend Animal." It means "A list of some type T, which I won't tell you what it is, but I know that T extends Animal." (In type theory, these are called existential types -- there exists a type T for which our List is a List<T>, but we don't necessarily know what T is.)
The difference is important. If you have "a list of things that all extend Animal", then it would be safe to add a Dog to the list -- but this is not what wildcards express. A List<? extends Animal> means "List of something, where that something extends Animal". It might be a List<Animal>, or a List<Dog>, or a List<Cat> -- we don't know. So we have no reason to think that its safe to add a Dog to that list -- because maybe my List is a List<Cat>.
In your example, you have a List<? super Animal>. This means, a list of some type T, where T is one of the supertypes of Animal. It might be a List<Object>, or a List<Animal>, or if Animal has a supertype HasLegs, it might be a List<HasLegs>. Here, it's safe to put a Dog into this list -- because whatever its a List of, Dog is definitely one of those (Animal, Object, etc), but when you take something out of the list, you have no idea whether its a Dog, an Animal, or just an Object.
List<? super Animal> aList is reference which can be used to handle List<Animal> , it can also be any superclass of Animal such as List<Object>.
In other words List<? super Animal> aList
- is not reference to some list which can store any supertype of Animal.
+ it is reference to list of some specific type which is supertype of Animal (including Animal itself) but you don't know which type it is exactly.
So it could be
List<Object> someList = new ArrayList<>();
someList.add(new Car());//OK since Car is a Object
List<? super Animal> aList = someList;// OK since Object is supertype of Animal
Because of that, code
Animal a = aList.get(0);//and where did that Car come from?
is not safe. Only safe type to store the result of get(0) is Object so you either need
Object o = aList.get(0);
or if you want to make sure that get will return Animal, change the type of your aList reference to List<Animal> or even List<? extends Animal>.
List<? super Animal> means a list of any superclass of Animal. If Animal is a subclass of LivingThing, then List<? super Animal> can be a list of Animals, a list of LivingThings, or a list of Object. In the last two cases, the assignment Animal x = aList.get(0) would of course be illegal, because you cannot assign a LivingThing to a reference of type Animal.
Consider the following method (it will not compile):
void f(List<? super Animal> list) {
Animal a = list.get(0); // line 1
list.add(new Object()); // line 2
}
The method call
List<Object> list = new ArrayList<>();
list.add(new Object());
f(list);
is correct, because Object is a superclass of Animal. However, if the method f would compile, you would try to assign in Line 1 an Object to an Animal reference, so it would not be type safe.
On the other, hand consider the method call:
f(new List<Animal>());
If the method f would compile, we would now be adding an Object instance to a list which should only contain Animals.
That's why both Line 1 and Line 2 are not allowed. If you have a class
public class A<T> {
public put(T in) { }
public T get() { }
}
then for the type A<? super Animal> the method get will return a Object (not an Animal), while the method put expects a parameter of type Animal.

Choosing best method in java generics using proper wildcard [duplicate]

I came across PECS (short for Producer extends and Consumer super) while reading up on generics.
Can someone explain to me how to use PECS to resolve confusion between extends and super?
tl;dr: "PECS" is from the collection's point of view. If you are only pulling items from a generic collection, it is a producer and you should use extends; if you are only stuffing items in, it is a consumer and you should use super. If you do both with the same collection, you shouldn't use either extends or super.
Suppose you have a method that takes as its parameter a collection of things, but you want it to be more flexible than just accepting a Collection<Thing>.
Case 1: You want to go through the collection and do things with each item.
Then the list is a producer, so you should use a Collection<? extends Thing>.
The reasoning is that a Collection<? extends Thing> could hold any subtype of Thing, and thus each element will behave as a Thing when you perform your operation. (You actually cannot add anything (except null) to a Collection<? extends Thing>, because you cannot know at runtime which specific subtype of Thing the collection holds.)
Case 2: You want to add things to the collection.
Then the list is a consumer, so you should use a Collection<? super Thing>.
The reasoning here is that unlike Collection<? extends Thing>, Collection<? super Thing> can always hold a Thing no matter what the actual parameterized type is. Here you don't care what is already in the list as long as it will allow a Thing to be added; this is what ? super Thing guarantees.
The principles behind this in computer science is called
Covariance: ? extends MyClass,
Contravariance: ? super MyClass and
Invariance/non-variance: MyClass
The picture below should explain the concept.
Picture courtesy: Andrey Tyukin
When dealing with collections, a common rule for selecting between upper or lower bounded wildcards is PECS. credit
PECS (Producer extends and Consumer super)
mnemonic → Get (extend) and Put (Super) principle.
This principle states that:
Use an extends wildcard when you only get values out of a structure.
Use a super wildcard when you only put values into a structure.
And don’t use a wildcard when you both get and put.
Example in Java:
class Super {
Number testCoVariance() {
return null;
}
void testContraVariance(Number parameter) {
}
}
class Sub extends Super {
#Override
Integer testCoVariance() {
return null;
} //compiles successfully i.e. return type is don't care(Integer is subtype of Number)
#Override
void testContraVariance(Integer parameter) {
} //doesn't support even though Integer is subtype of Number
}
The Liskov Substitution Principle (LSP) states that “objects in a program should be replaceable with instances of their subtypes without altering the correctness of that program”.
Within the type system of a programming language, a typing rule
covariant if it preserves the ordering of types (≤), which orders types from more specific to more generic;
contravariant if it reverses this ordering;
invariant or nonvariant if neither of these applies.
Covariance and contravariance
Read-only data types (sources) can be covariant;
write-only data types (sinks) can be contravariant.
Mutable data types which act as both sources and sinks should be invariant.
To illustrate this general phenomenon, consider the array type. For the type Animal we can make the type Animal[]
covariant: a Cat[] is an Animal[];
contravariant: an Animal[] is a Cat[];
invariant: an Animal[] is not a Cat[] and a Cat[] is not an Animal[].
Java Examples:
Object name= new String("prem"); //works
List<Number> numbers = new ArrayList<Integer>();//gets compile time error
Integer[] myInts = {1,2,3,4};
Number[] myNumber = myInts;
myNumber[0] = 3.14; //attempt of heap pollution i.e. at runtime gets java.lang.ArrayStoreException: java.lang.Double(we can fool compiler but not run-time)
List<String> list=new ArrayList<>();
list.add("prem");
List<Object> listObject=list; //Type mismatch: cannot convert from List<String> to List<Object> at Compiletime
more examples
Image src
bounded(i.e. heading toward somewhere) wildcard : There are 3 different flavours of wildcards:
In-variance/Non-variance: ? or ? extends Object - Unbounded Wildcard. It stands for the family of all types. Use when you both get and put.
Co-variance: ? extends T ( Reign of T descendants) - a wildcard with an upper bound. T is the upper-most class in the inheritance hierarchy. Use an extends wildcard when you only Get values out of a structure.
Contra-variance: ? super T ( Reign of T ancestor) - a wildcard with a lower bound. T is the lower-most class in the inheritance hierarchy. Use a super wildcard when you only Put values into a structure.
Note: wildcard ? means zero or one time, represents an unknown type. The wildcard can be used as the type of a parameter, never used as a type argument for a generic method invocation, a generic class instance creation.(i.e. when used wildcard that reference not used in elsewhere in program like we use T)
import java.util.ArrayList;
import java.util.List;
class Shape { void draw() {}}
class Circle extends Shape {void draw() {}}
class Square extends Shape {void draw() {}}
class Rectangle extends Shape {void draw() {}}
public class Test {
public static void main(String[] args) {
//? extends Shape i.e. can use any sub type of Shape, here Shape is Upper Bound in inheritance hierarchy
List<? extends Shape> intList5 = new ArrayList<Shape>();
List<? extends Shape> intList6 = new ArrayList<Cricle>();
List<? extends Shape> intList7 = new ArrayList<Rectangle>();
List<? extends Shape> intList9 = new ArrayList<Object>();//ERROR.
//? super Shape i.e. can use any super type of Shape, here Shape is Lower Bound in inheritance hierarchy
List<? super Shape> inList5 = new ArrayList<Shape>();
List<? super Shape> inList6 = new ArrayList<Object>();
List<? super Shape> inList7 = new ArrayList<Circle>(); //ERROR.
//-----------------------------------------------------------
Circle circle = new Circle();
Shape shape = circle; // OK. Circle IS-A Shape
List<Circle> circles = new ArrayList<>();
List<Shape> shapes = circles; // ERROR. List<Circle> is not subtype of List<Shape> even when Circle IS-A Shape
List<? extends Circle> circles2 = new ArrayList<>();
List<? extends Shape> shapes2 = circles2; // OK. List<? extends Circle> is subtype of List<? extends Shape>
//-----------------------------------------------------------
Shape shape2 = new Shape();
Circle circle2= (Circle) shape2; // OK. with type casting
List<Shape> shapes3 = new ArrayList<>();
List<Circle> circles3 = shapes3; //ERROR. List<Circle> is not subtype of List<Shape> even Circle is subetype of Shape
List<? super Shape> shapes4 = new ArrayList<>();
List<? super Circle> circles4 = shapes4; //OK.
}
/*
* Example for an upper bound wildcard (Get values i.e Producer `extends`)
*
* */
public void testCoVariance(List<? extends Shape> list) {
list.add(new Object());//ERROR
list.add(new Shape()); //ERROR
list.add(new Circle()); // ERROR
list.add(new Square()); // ERROR
list.add(new Rectangle()); // ERROR
Shape shape= list.get(0);//OK so list act as produces only
/*
* You can't add a Shape,Circle,Square,Rectangle to a List<? extends Shape>
* You can get an object and know that it will be an Shape
*/
}
/*
* Example for a lower bound wildcard (Put values i.e Consumer`super`)
* */
public void testContraVariance(List<? super Shape> list) {
list.add(new Object());//ERROR
list.add(new Shape());//OK
list.add(new Circle());//OK
list.add(new Square());//OK
list.add(new Rectangle());//OK
Shape shape= list.get(0); // ERROR. Type mismatch, so list acts only as consumer
Object object= list.get(0); //OK gets an object, but we don't know what kind of Object it is.
/*
* You can add a Shape,Circle,Square,Rectangle to a List<? super Shape>
* You can't get an Shape(but can get Object) and don't know what kind of Shape it is.
*/
}
}
generics and examples
Covariance and contravariance determine compatibility based on types. In either case, variance is a directed relation. Covariance can be translated as "different in the same direction," or with-different, whereas contravariance means "different in the opposite direction," or against-different. Covariant and contravariant types are not the same, but there is a correlation between them. The names imply the direction of the correlation.
https://stackoverflow.com/a/54576828/1697099
https://stackoverflow.com/a/64888058/1697099
Covariance: accept subtypes (read only i.e. Producer)
Contravariance: accept supertypes (write only i.e. Consumer)
public class Test {
public class A {}
public class B extends A {}
public class C extends B {}
public void testCoVariance(List<? extends B> myBlist) {
B b = new B();
C c = new C();
myBlist.add(b); // does not compile
myBlist.add(c); // does not compile
A a = myBlist.get(0);
}
public void testContraVariance(List<? super B> myBlist) {
B b = new B();
C c = new C();
myBlist.add(b);
myBlist.add(c);
A a = myBlist.get(0); // does not compile
}
}
In a nutshell, three easy rules to remember PECS:
Use the <? extends T> wildcard if you need to retrieve object of
type T from a collection.
Use the <? super T> wildcard if you need to put objects of type T in
a collection.
If you need to satisfy both things, well, don’t use any wildcard. As
simple as that.
As I explain in my answer to another question, PECS is a mnemonic device created by Josh Bloch to help remember Producer extends, Consumer super.
This means that when a parameterized type being passed to a method will produce instances of T (they will be retrieved from it in some way), ? extends T should be used, since any instance of a subclass of T is also a T.
When a parameterized type being passed to a method will consume instances of T (they will be passed to it to do something), ? super T should be used because an instance of T can legally be passed to any method that accepts some supertype of T. A Comparator<Number> could be used on a Collection<Integer>, for example. ? extends T would not work, because a Comparator<Integer> could not operate on a Collection<Number>.
Note that generally you should only be using ? extends T and ? super T for the parameters of some method. Methods should just use T as the type parameter on a generic return type.
let's assume this hierarchy:
class Creature{}// X
class Animal extends Creature{}// Y
class Fish extends Animal{}// Z
class Shark extends Fish{}// A
class HammerSkark extends Shark{}// B
class DeadHammerShark extends HammerSkark{}// C
Let's clarify PE - Producer Extends:
List<? extends Shark> sharks = new ArrayList<>();
Why you cannot add objects that extend "Shark" in this list? like:
sharks.add(new HammerShark());//will result in compilation error
Since you have a list that can be of type A, B or C at runtime, you cannot add any object of type A, B or C in it because you can end up with a combination that is not allowed in java. In practice, the compiler can indeed see at compiletime that you add a B:
sharks.add(new HammerShark());
...but it has no way to tell if at runtime, your B will be a subtype or supertype of the list type. At runtime the list type can be any of the types A, B, C. So you cannot end up adding HammerSkark (super type) in a list of DeadHammerShark for example.
*You will say: "OK, but why can't I add HammerSkark in it since it is the smallest type?".
Answer: It is the smallest you know. But HammerSkark can be extended too by somebody else and you end up in the same scenario.
Let's clarify CS - Consumer Super:
In the same hierarchy we can try this:
List<? super Shark> sharks = new ArrayList<>();
What and why you can add to this list?
sharks.add(new Shark());
sharks.add(new DeadHammerShark());
sharks.add(new HammerSkark());
You can add the above types of objects because anything below shark(A,B,C) will always be subtypes of anything above shark (X,Y,Z). Easy to understand.
You cannot add types above Shark, because at runtime the type of added object can be higher in hierarchy than the declared type of the list(X,Y,Z). This is not allowed.
But why you cannot read from this list?
(I mean you can get an element out of it, but you cannot assign it to anything other than Object o):
Object o;
o = sharks.get(2);// only assignment that works
Animal s;
s = sharks.get(2);//doen't work
At runtime, the type of list can be any type above A: X, Y, Z, ...
The compiler can compile your assignment statement (which seems correct) but, at runtime the type of s (Animal) can be lower in hierarchy than the declared type of the list(which could be Creature, or higher). This is not allowed.
To sum up
We use <? super T> to add objects of types equal or below T to the List. We cannot read from
it.
We use <? extends T> to read objects of types equal or below T from list. We cannot add element to it.
let’s try visualizing this concept.
<? super SomeType> is an “undefined(yet)” type, but that undefined type should be a superclass of the ‘SomeType’ class.
The same goes for <? extends SomeType>. It’s a type that should extend the ‘SomeType’ class (it should be a child class of the ‘SomeType’ class).
If we consider the concept of 'class inheritance' in a Venn diagram, an example would be like this:
Mammal class extends Animal class (Animal class is a superclass of Mammal class).
Cat/Dog class extends Mammal class (Mammal class is a superclass of Cat/Dog class).
Then, let’s think about the ‘circles’ in the above diagram as a ‘box’ that has a physical volume.
You CAN’T put a bigger box into a smaller one.
You can ONLY put a smaller box into a bigger one.
When you say <? super SomeType>, you wanna describe a ‘box’ that is the same size or bigger than the ‘SomeType’ box.
If you say <? extends SomeType>, then you wanna describe a ‘box’ that is the same size or smaller than the ‘SomeType’ box.
so what is PECS anyway?
An example of a ‘Producer’ is a List which we only read from.
An example of a ‘Consumer’ is a List which we only write into.
Just keep in mind this:
We ‘read’ from a ‘producer’, and take that stuff into our own box.
And we ‘write’ our own box into a ‘consumer’.
So, we need to read(take) something from a ‘producer’ and put that into our ‘box’. This means that any boxes taken from the producer should NOT be bigger than our ‘box’. That’s why “Producer Extends.”
“Extends” means a smaller box(smaller circle in the Venn diagram above). The boxes of a producer should be smaller than our own box, because we are gonna take those boxes from the producer and put them into our own box. We can’t put anything bigger than our box!
Also, we need to write(put) our own ‘box’ into a ‘consumer’. This means that the boxes of the consumer should NOT be smaller than our own box. That’s why “Consumer Super.”
“Super” means a bigger box(bigger circle in the Venn diagram above). If we want to put our own boxes into a consumer, the boxes of the consumer should be bigger than our box!
Now we can easily understand this example:
public class Collections {
public static <T> void copy(List<? super T> dest, List<? extends T> src) {
for (int i = 0; i < src.size(); i++)
dest.set(i, src.get(i));
}
}
In the above example, we want to read(take) something from src and write(put) them into dest. So the src is a “Producer” and its “boxes” should be smaller(more specific) than some type T.
Vice versa, the dest is a “Consumer” and its “boxes” should be bigger(more general) than some type T.
If the “boxes” of the src were bigger than that of the dest, we couldn’t put those big boxes into the smaller boxes the dest has.
If anyone reads this, I hope it helps you better understand
“Producer Extends, Consumer Super.”
Happy coding! :)
This is the clearest, simplest way for me think of extends vs. super:
extends is for reading
super is for writing
I find "PECS" to be a non-obvious way to think of things regarding who is the "producer" and who is the "consumer". "PECS" is defined from the perspective of the data collection itself – the collection "consumes" if objects are being written to it (it is consuming objects from calling code), and it "produces" if objects are being read from it (it is producing objects to some calling code). This is counter to how everything else is named though. Standard Java APIs are named from the perspective of the calling code, not the collection itself. For example, a collection-centric view of java.util.List should have a method named "receive()" instead of "add()" – after all, the calling code adds the element, but the list itself receives the element.
I think it's more intuitive, natural and consistent to think of things from the perspective of the code that interacts with the collection – does the code "read from" or "write to" the collection? Following that, any code writing to the collection would be the "producer", and any code reading from the collection would be the "consumer".
(adding an answer because never enough examples with Generics wildcards)
// Source
List<Integer> intList = Arrays.asList(1,2,3);
List<Double> doubleList = Arrays.asList(2.78,3.14);
List<Number> numList = Arrays.asList(1,2,2.78,3.14,5);
// Destination
List<Integer> intList2 = new ArrayList<>();
List<Double> doublesList2 = new ArrayList<>();
List<Number> numList2 = new ArrayList<>();
// Works
copyElements1(intList,intList2); // from int to int
copyElements1(doubleList,doublesList2); // from double to double
static <T> void copyElements1(Collection<T> src, Collection<T> dest) {
for(T n : src){
dest.add(n);
}
}
// Let's try to copy intList to its supertype
copyElements1(intList,numList2); // error, method signature just says "T"
// and here the compiler is given
// two types: Integer and Number,
// so which one shall it be?
// PECS to the rescue!
copyElements2(intList,numList2); // possible
// copy Integer (? extends T) to its supertype (Number is super of Integer)
private static <T> void copyElements2(Collection<? extends T> src,
Collection<? super T> dest) {
for(T n : src){
dest.add(n);
}
}
The PECS "rule" just ensures that the following is legal:
Consumer: whatever ? is, it can legally refer to T
Producer: whatever ? is, it can legally be referred to by T
The typical pairing along the lines of List<? extends T> producer, List<? super T> consumer is simply ensuring that the compiler can enforce the standard "IS-A" inheritance relationship rules. If we could do so legally, it might be simpler to say <T extends ?>, <? extends T> (or better yet in Scala, as you can see above, it's [-T], [+T]. Unfortunately the best we can do is <? super T>, <? extends T>.
When I first encountered this and broke it down in my head the mechanics made sense but the code itself continued to look confusing to me - I kept thinking "it seems like the bounds shouldn't need to be inverted like that" - even though I was clear on the above - that it's simply about guaranteeing compliance with the standard rules of reference.
What helped me was looking at it using ordinary assignment as an analogy.
Consider the following (not production ready) toy code:
// copies the elements of 'producer' into 'consumer'
static <T> void copy(List<? extends T> producer, List<? super T> consumer) {
for(T t : producer)
consumer.add(t);
}
Illustrating this in terms of the assignment analogy, for consumer the ? wildcard (unknown type) is the reference - the "left hand side" of the assignment - and <? super T> ensures that whatever ? is, T "IS-A" ? - that T can be assigned to it, because ? is a super type (or at most the same type) as T.
For producer the concern is the same it's just inverted: producer's ? wildcard (unknown type) is the referent - the "right hand side" of the assignment - and <? extends T> ensures that whatever ? is, ? "IS-A" T - that it can be assigned to a T, because ? is a sub type (or at least the same type) as T.
Covariance: accept subtypes
Contravariance: accept supertypes
Covariant types are read-only, while contravariant types are write-only.
Remember this:
Consumer eat supper(super); Producer extends his parent's factory
PECS (Producer extends and Consumer super)
[Covariance and contravariance]
Lets take a look at example
public class A { }
//B is A
public class B extends A { }
//C is A
public class C extends A { }
Generics allows you to work with Types dynamically in a safe way
//ListA
List<A> listA = new ArrayList<A>();
//add
listA.add(new A());
listA.add(new B());
listA.add(new C());
//get
A a0 = listA.get(0);
A a1 = listA.get(1);
A a2 = listA.get(2);
//ListB
List<B> listB = new ArrayList<B>();
//add
listB.add(new B());
//get
B b0 = listB.get(0);
Problem
Since Java's Collection is a reference type as a result we have next issues:
Problem #1
//not compiled
//danger of **adding** non-B objects using listA reference
listA = listB;
*Swift's generic does not have such problem because Collection is Value type[About] therefore a new collection is created
Problem #2
//not compiled
//danger of **getting** non-B objects using listB reference
listB = listA;
The solution - Generic Wildcards
Wildcard is a reference type feature and it can not be instantiated directly
Solution #1
<? super A> aka lower bound aka contravariance aka consumers guarantees that it is operates by A and all superclasses, that is why it is safe to add
List<? super A> listSuperA;
listSuperA = listA;
listSuperA = new ArrayList<Object>();
//add
listSuperA.add(new A());
listSuperA.add(new B());
//get
Object o0 = listSuperA.get(0);
Solution #2
<? extends A> aka upper bound aka covariance aka producers guarantees that it is operates by A and all subclasses, that is why it is safe to get and cast
List<? extends A> listExtendsA;
listExtendsA = listA;
listExtendsA = listB;
//get
A a0 = listExtendsA.get(0);
Using real life example (with some simplifications):
Imagine a freight train with freight cars as analogy to a list.
You can put a cargo in a freight car if the cargo has the same or smaller size than the freight car = <? super FreightCarSize>
You can unload a cargo from a freight car if you have enough place (more than the size of the cargo) in your depot = <? extends DepotSize>
PECS: Producer extends and Consumer super
Prerequisites for understanding:
Generics and generic wildcards
Polymorphism, Subtyping and Supertyping
Lets say we have a type which takes a generic type parameter T, for example List<T>. When we write code it can be potentially beneficial to also allow subtypes or supertypes of our generic type parameter T. This relaxes the restraints for the user of the API and can make the code more flexible.
Let first see what we gain relaxing these restrictions. Lets say we have the following 3 classes:
class BaseAnimal{};
class Animal extends BaseAnimal{};
class Duck extends Animal{};
and we are building a public method which takes a list<Animal>
If we use a super List<? super Animal> instead of List<Animal> we now can pass in more lists to satisfy the requirement of our method. We now can pass in either List<Animal> or List<BaseAnimal> even List<Object>
If we use an extends List<? extends Animal> instead of List<Animal> we now can pass in more lists to satisfy the requirement of our method. We now can pass in either List<Animal> or List<Duck>
However this poses the following 2 restrictions:
If we use a super type like List<? super Animal> we don't know the exact type of List<T> it will be. It could be either a list of List<Animal> or List<BaseAnimal> or List<Object>. We have no way of knowing. This means we can never get a value out of this List because we do not know what the type will be. However we can put any data type which is Animal or extends it into the List. Because we can only put data into the List it is called a consumer of data.
If we use an extends List<? extends Animal> instead of List<Animal>. We also don't know what the exact type is. It can either be List<Animal> or List<Duck>. We can't add something to the List now because we can never certainly know what the type is. However we can pull something out because we always know that anything which comes out of the list is a subtype of Animal. Because we can only pull data out of the List is it called a producer of data.
Here is a simple program to illustrate the relaxation of the type restrictions:
import java.util.ArrayList;
import java.util.List;
public class Generics {
public static void main(String[] args) {
Generics generics = new Generics();
generics.producerExtends(new ArrayList<Duck>());
generics.producerExtends(new ArrayList<Animal>());
generics.consumerSuper(new ArrayList<Object>());
generics.consumerSuper(new ArrayList<Animal>());
}
// ? extends T is an upper bound
public void producerExtends (List<? extends Animal> list) {
// Following are illegal since we never know exactly what type the list will be
// list.add(new Duck());
// list.add(new Animal());
// We can read from it since we are always getting an Animal or subclass from it
// However we can read them as an animal type, so this compiles fine
if (list.size() > 0) {
Animal animal = list.get(0);
}
}
// ? extends T is a lower bound
public void consumerSuper (List<? super Animal> list) {
// It will be either a list of Animal or a superclass of it
// Therefore we can add any type which extends animals
list.add(new Duck());
list.add(new Animal());
// Compiler won't allow this it could potentially be a super type of Animal
// Animal animal = list.get(0);
}

Add Strings through use of generic 'extends' causes compiler error

Below code :
List<? extends String> genericNames = new ArrayList<String>();
genericNames.add("John");
Gives compiler error :
Multiple markers at this line
- The method add(capture#1-of ? extends String) in the type List is not applicable for the
arguments (String)
- The method add(capture#1-of ?) in the type List is not applicable for the arguments (String)
What is causing this error ? Should I not be able to add Strings or its subtype since I am extending String within the type parameter ?
When you use wildcards with extends, you can't add anything in the collection except null. Also, String is a final class; nothing can extend String.
Reason: If it were allowed, you could just be adding the wrong type into the collection.
Example:
class Animal {
}
class Dog extends Animal {
}
class Cat extends Animal {
}
Now you have List<? extends Animal>
public static void someMethod(List<? extends Animal> list){
list.add(new Dog()); //not valid
}
and you invoke the method like this:
List<Cat> catList = new ArrayList<Cat>();
someMethod(catList);
If it were allowed to add in the collection when using wildcards with extends, you just added a Dog into a collection which accepts only Cat or subtype type. Thus you can't add anything into the collection which uses wildcards with upper bounds.
String is a final class and cannot be extended. Additionally, for the case you seem to be interested in, you do not need the extends keyword. List<String> will do what you seem to want. That will allow Strings and sub-classes of String (if such a thing could exist, which it can't since String is final).
Just want to add to the answer of GanGnaMStYleOverFlow that you can add an object of any subtype of Animal to the following list:
List<Animal> animals = new ArrayList<Animal>();
You should use such list whenever you think that it can contain any kind of animals.
On the other hand, you should use List<? extends Animal> when you want to specify that the list contains some kind of animal but you don't know which one. Since you don't know what kind of animals are there, you cannot add any.

Generic lower unbound vs upper bounded wildcards

import java.util.List;
import java.util.ArrayList;
interface Canine {}
class Dog implements Canine {}
public class Collie extends Dog {
public static void main(String[] args){
List<Dog> d = new ArrayList<Dog>();
List<Collie> c = new ArrayList<Collie>();
d.add(new Collie());
c.add(new Collie());
do1(d); do1(c);
do2(d); do2(c);
}
static void do1(List<? extends Dog> d2){
d2.add(new Collie());
System.out.print(d2.size());
}
static void do2(List<? super Collie> c2){
c2.add(new Collie());
System.out.print(c2.size());
}
}
The answer for this question tell that when a method takes a wildcard generic typ, the collection can be accessed or modified, but not both. (Kathy and Bert)
What does it mean 'when a method takes a wildcard generic typ, the collection can be accessed or modified, but not both' ?
As far as I know,
The method do1 has List<? extends Dog> d2 so d2 only can be accessed but not modified.
The method d2 has List<? super Collie> c2 so c2 can be accessed and modified and there is no compilation error.
Generic guidelines
You cannot add a Cat to a List<? extends Animal> because you don't know what kind of list that is. That could be a List<Dog> also. So you don't want to throw your Cat into a Black Hole. That is why modification of List declared that way is not allowed.
Similarly when you fetch something out of a List<? super Animal>, you don't know what you will get out of it. You can even get an Object, or an Animal. But, you can add an Animal safely in this List.
I pasted your code into my IDE. The following error was signalled inside do1:
The method add(capture#1-of ? extends Dog) in the type List is not applicable for the arguments (Collie)
This is, of course, as expected.
You simply cannot add a Collie to a List<? extends Dog> because this reference may hold for example a List<Spaniel>.
The answer for this question tell that when a method takes a wildcard generic typ, the collection can be accessed or modified, but not both. (Kathy and Bert)
That's a fair first approximation, but not quite correct. More correct would be:
You can only add null to a Collection<? extends Dog> because its add method takes an argument of ? extends Dog. Whenever you invoke a method, you must pass parameters that are of a subtype of the declared parameter type; but for the parameter type ? extends Dog, the compiler can only be sure that the argument is of compatible type if the expression is null. However, you can of course modify the collection by calling clear() or remove(Object).
On the other hand, if you read from a Collection<? super Dog>, its iterator has return type ? super Dog. That is, it will return objects that are a subtype of some unknown supertype of Dog. But differently, the Collection could be a Collection<Object> containing only instances of String. Therefore
for (Dog d : collection) { ... } // does not compile
so the only thing we know is that instances of Object are returned, i.e. the only type-correct way of iterating such a Collection is
for (Object o : collection) { ... }
but it is possible to read from a collection, you just don't know what types of objects you will get.
We can easily generalize that observation to: Given
class G<T> { ... }
and
G<? extends Something> g;
we can only pass null to method parameters with declared type T, but we can invoke methods with return type T, and assign the result a variable of type Something.
On the other hand, for
G<? super Something> g;
we can pass any expression of type Something to method parameters with declared type T, and we can invoke methods with return type T, but only assign the result to a variable of type Object.
To summarize, the restrictions on the use of wildcard types only depend on the form of the method declarations, not on what the methods do.
I pasted your code into IDEONE http://ideone.com/msMcQ. It did not compile for me - which is what I expected. Are you sure you did not have any compilation errors?

Categories