Maps of generic lists not converting nicely - java

Rather than attempt this in words, I'll just give an example:
I have an Animal class, as well as a Dog,Fish,Cat, etc. which extend Animal.
I have three different methods, which return Map<String,List<Dog>>, Map<String,List<Fish>>, Map<String,List<Cat>>. We'll call these getDogMap, getCatMap, and getFishMap.
I am writing a generic method which, depending on various parameters, calls one of these methods. Here is what I expected to be allowed to do:
public void <A extends Animal> doSomething(){
Map<String,List<A>> someMap;
if(someCondition){
someMap = getDogMap();
}else if(anotherCondition){
someMap = getFishMap();
}else{
someMap = getCatMap():
}
}
Or at least, that with casting ala someMap = (Map<String,List<Dog>>) getDogMap();
However, this does not work. Eclipse tells me "Type mismatch: cannot convert from Map<String,List<Dog>> to Map<String,List<A>>" If I try to force the cast, it tells me "Cannot cast from Map<STring,List<Dog>> to Map<String,List<A>>".
What am I doing wrong?

public void <A extends Animal> doesn't mean "A is any type that extends Animal", it means "A is a specific one of the types that extends Animal". You need to use the following declarations:
public void doSomething() {
Map<String, ? extends List<? extends Animal>> someMap;
// ...
}
The construct ? extends Animal is how you express "any type that extends Animal".
The reason you have to use that declaration is that, counter-intuitively, the way subtype relationships between generic types work isn't exactly consistent with how they work between regular types. For instance, List<Dog> is a subtype of Collection<Dog>. It is not a subtype of List<Animal>, or Collection<Animal> etc. The reason why this isn't allowed is called heap pollution, also explained in Angelika Langer's FAQ. List<Dog> is, however, a subtype of List<? extends Animal>. A variable of type List<? extends Animal> may have assigned a List<Dog>, or a List<Cat>, or a List<Animal>. The important part is that the compiler doesn't know which of these it is, just that it is one of them.
Just like List<Dog> is not a subtype of List<Animal>, analogously it holds that Map<String, List<Dog>> is not a subtype of Map<String, List<? extends Animal>>.
The best way to demonstrate why generics work this way is proof by contradiction; that is, showing examples of (broken) code that would lead to errors were generics to work "intuitively". So, if List<Dog> were a subtype of List<Animal>, the following code would be valid:
List<Dog> dogs = new ArrayList<Dog>();
List<Animal> animals = dogs; // unsafe cast
// this operation violates type safety
animals.add(new Cat());
// would assign a Cat to a variable of type Dog without a compile error!
Dog dog = animals.get(0);
Similarly, for your Map:
Map<String, List<Dog>> dogses = new HashMap<String, List<Dog>>();
Map<String, List<? extends Animal>> animalses = dogses; // unsafe cast
List<Cat> cats = new ArrayList();
cats.put(new Cat());
animalses.put("cats", cats);
List<Dog> dogs = dogses.get("cats");
Dog dog = dogs.get(0); // uh-oh

The problem here lies in the way generic types work.
In Java, you would expect that Number is a superclass of Integer (and you would be right). You would also expect that Number[] is a superclass of Integer[]. You would also be right. However, you cannot say that List<Number> is a superclass of List<Integer>. This violates type safety.
List<Integer> intList = new ArrayList<Integer>();
List<Number> numList = intList; // the system stops you here
numList.add(Math.PI); // so that you don't do this
In you case, you have a type Animal and its subtypes Dog, Cat, and Fish. Extend this logic to your animals, and you see why you are having trouble casting List<Dog> to List<A>.
Now, you can use the addAll(Collection) method defined in the List interface.
List<Animal> animals = new List<Animal>();
List<Dog> dogs = new List<Dog>();
animals.addAll(dogs);

Related

Why does the java compiler need a seemingly useless wrapper function to type check the generic bounds? [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);
}

Why can't List<? extends Animal> be replaced with List<Animal>?

Consider the following code:
public class Main {
static class Animal {}
static class Dog extends Animal {}
static List<? extends Animal> foo() {
List<Dog> dogs = new ArrayList<>();
return dogs;
}
public static void main(String[] args) {
List<Animal> dogs = Main.foo(); // compile error
}
}
I'm trying to understand why it won't compile. Meaning, why doesn't the compiler let me refer to List<? extends Animal> as a List<Animal>?
Is that has something to do with the type erasure mechanism?
A List<Animal> is a List to which you can add any Animal (or null), and everything you take out of it will be an Animal.
A List<? extends Animal> is a list which contains only a specific subclass of Animal (or null), and you don't know which one; this allows you to treat everything you take out of it as an Animal, but you aren't allowed to add anything to it (except for literal null).
A List<? extends Animal> can't act as a List<Animal>, because that would allow you to do this:
List<Cat> listOfCats = new ArrayList<>();
List<? extends Animal> listOfSomeAnimals = listOfCats; // Fine.
List<Animal> listOfAnimals = listOfSomeAnimals; // Error, pretend it works.
listOfAnimals.add(new Dog());
Now, because listOfCats, listOfSomeAnimals and listOfAnimals are all the same list, the Dog has been added to listOfCats. As such:
Cat cat = listOfCats.get(0); // ClassCastException.
Because List<? extends Animal> would allow any subclass of Animal. List would simply allow objects of the Animal class.
In List<? extends Animal> are also objects like cat or dog allowed. If you initiate this with a "pure" dog-list, you can't tell from the outside that it's not allowed and therefore it doesn't compile.
Co-, Contra- & Invariance in Java
This is about Co-, Contra- and Invariance. Covariance tells us about what we can take out, Contravariance tells us about what we can put in, and Invariance tells us about both.
Invariance
List<Animal> is invariant. You can add any Animal, and you are guaranteed to get any Animal out - get(int) gives us an Animal, and add(Animal) must accept any Animal. We can put an Animal in, we get an Animal out.
List<Animal> animals = new ArrayList<Dog>() is a compiler error, since it doesn't accept Animal or Cat. get(int) still gives us only Animals (Dogs are Animals, after all), but not accepting the others is a deal-breaker.
List<Animal> animals = new ArrayList<Object>() is likewise a deal-breaker. Yes, it accepts any animal (we can put Animals in), but it gives us Objects.
Contravariance
List<? super Dog> is contravariant. We can only put Dogs in, put nothing is said about what we get out. Thus, we get Object out.
List<? super Dog> dogs = new ArrayList<Animal>(); this works, because we can put a Dog into it. And Animals are Objects, so we can get objects out.
List<? super Dog> dogs = new ArrayList<Animal>();
// dogs.add(new Animal()); // compile error, need to put Dog in
dogs.add(new Dog());
Object obj = dogs.get(0);
// Dog dog = dogs.get(0); // compile error, can only take Object out
Covariance
List<? extends Animal> is covariant. You are guaranteed to get an Animal out.
List<? extends Animal> animals = new ArrayList<Cat>(); works, because cats are Animals, and get(n) gives you Animals. Granted, they are all Cats, but Cats are Animals, so this works out fine.
Adding stuff is harder, though, since you don't actually have a type that you can put in:
List<? extends Animal> animals = new ArrayList<Cat>();
//animals.add(new Cat()); // compile error
//animals.add(new Animal()); // compile error
Animal animal = animals.get(0);
List<? extends Cat> cats = new ArrayList<Animal>(); is a compiler error, because you can take out any animal - but you require that the only thing that can be taken out is Cats.
Your code
static List<? extends Animal> foo() {
List<Dog> dogs = new ArrayList<>();
return dogs;
}
Here, everything is fine. foo() is a List where you can take out Animals. You surely Since Dogs are Animals, and you can take out Dogs, you you can take out Animals. Everything you take out of the List is guaranteed to be an Animal.
List<Animal> dogs = Main.foo(); // compile error
You are saying that dogs is a List where you can put in any Animal, and you are guaranteed to get Animals out. The last part is easy, yes, you are guaranteed to get Animals out, that is what ? extends Animal means. But you can't put arbitrary Animals in. And that is why this fails.

Why can’t we read from contravariance type

I’m learning generics in Java, and I am learning Covariance and contravariance. I understood covariance and why can’t we write in to covariance type. But for me contravariance is confusing. I want to know why reading from contravariance is always Object type
Lets say we have the following classes
Class Animal
Class Dog extends Animal
Class Tiger extends Animal
Now, consider the following code
List<Animal> animals = new ArrayList<>();
List<? super Animal> contraVarianceList=animals;
animals.add(new Dog()); // Allowed
animals.add(new Tiger()); //Allowed
Animal animal = contraVarianceList.get(0); // Not allowed. Why?
My question is why cant we read “Animal” from the contravariance list. Why is it always and only “Object” that can be returned? What will or may go wrong if we read “Animal” from the contravariance list?
I presume you meant to ask why is Animal animal=contraVarianceList.get(0); not allowed.
Think about it this way:
List<Animal> animals= new ArrayList<>();
List<Object> objects = new ArrayList<>();
List<? super Animal> contraVarianceList = animals;
List<? super Animal> contraVarianceList2 = objects;
animals.add(new Dog()); // Allowed
animals.add(new Tiger()); //Allowed
objects.add(new Dog()); // Allowed
objects.add(new Tiger()); //Allowed
//there is no type difference between contraVarianceList and contraVarianceList2
Animal animal = contraVarianceList.get(0); //compiler will complain
Animal animal2 = contraVarianceList2.get(0); //compiler will complain
The compiler cannot really know that any of them is carrying only Animal instances. You explicitly said ? super Animal so it could also be List that is carrying plain Object.
Usually you use these types of lists for parameters of methods where you are expecting to add items rather than read from the list. So having something like this:
void populateAnimals(List<? super Animal> animals) {
//whatever code
animals.add(...);
}
Guarantees that you can use the method with both List<Object> and List<Animal>.

Can Java generic merges 2 lists of different sub-class into one list of super class?

Say I have Animal class and Cat and Dog as its 2 subclass.
I want to return a list of both pets in one java method. So I tried the following:
public List<? extends Animal> getAllPets(...) {
List<? extends Animal> allDogs = findAnimals(...someOtherArgs, Dog.class);
List<? extends Animal> allCats = findAnimals(...someOtherArgs, Cat.class);
List<? extends Animal> retObjs = allDogs;
retObjs.addAll(allCats); // won't compile
return retObjs;
}
Is it possible to do that in Java?
Try such way:
public List<? extends Animal> getAllPets(...) {
List<Dog> allDogs = findDogs();
List<Cat> allCats = findCats();
List<Animal> retObjs = new ArrayList<>(allDogs.size() + allCats.size());
retObjs.addAll(allDogs);
retObjs.addAll(allCats);
return retObjs;
}
Simply leave out the wildcard, like this:
List<Integer> a = Arrays.asList(1);
List<Double> b = Arrays.asList(1.0);
List<Number> c = new ArrayList<>();
c.addAll(a);
c.addAll(b);
System.out.println(c);
The above compiles and runs fine, and prints
[1, 1.0]
Polymorphism allows you to store all instances of any child classes in a List which was declared as list to store object of BaseClass. So, in your case, you need to declare List<Animal>, now you can store here any Cat or Dog.
It's no need to use ? in your code. Delete them as the code below:
public List<? extends Animal> getAllPets(...) {
List<Animal> allDogs = findDogs();
List<Animal> allCats = findCats();
List<Animal> retObjs = allDogs;
retObjs.addAll(allCats); // won't compile
return retObjs;
}

Wildcards in java Generics

I know Generics are invariant: for any two distinct types Type1 and Type2,
List< Type1> is neither a subtype nor a supertype of List<
Type2>
so
List<someObject> nums = new ArrayList<someObject>(); // this is correct
List<Object> nums = new ArrayList<subObject>(); // this is not correct
but
List<Number> nums = new ArrayList<Number>();
List<? super Number> sink = nums; ????(line 2)
let say if wildcard is Object so line 2 wil be
List<Object> sink = List<Number> nums
It seems that the invariant rule not applied here
can anyone explain to me why line 2 is compiled without error ?
Thank so much
If I am not wrong, you wish to have an explanation on how is the below valid code:
List<Number> nums = new ArrayList<Number>();
List<? super Number> sink = nums;
Invariance is property of the class on how its type parameter affects its subtyping.
Generics are invariant, but wild cards exist to help us with with sub-typing. They are not very useful as they dont representing anytype but represent a good hack. Below is valid
List<Animal> <: List<?>
List<?> <: List
Better example:
List<? super Animal> d1= new ArrayList<Animal>();
d1.add(new Animal());
d1.add(new Dog());
The above works, because d1 is of type List<? super Animal>. You can imagine add function to behave like:
boolean add(? super Animal e);
So to add method you can pass any variable that is subtype of ? super Animal. And
Animal <: ? super Animal
Dog <: ? super Animal
So adding a Dog or Animal works. So d1 acts as a list that can take any parameter of type Animal or subtype if it.
Similarly, you can also have below. But technically you cant add anything to this list. If there existed sometype in java that is subtype of every type, then you could properly add element of that type to it. Nothing Else.
ArrayList<? extends Animal> d1 = new ArrayList<Animal>();
Look at this answer for more info.
The invariant rule does indeed apply. The statement:
List<? super Number> sink = nums;
just doesn't work they way you are thinking.
I expect that you think that you can assign an object of type ArrayList to a variable of type List<? super Number> because the former is a subclass of the latter. This would break the invariant rule, but this is not what is happening.
The List<? super Number> variable represents the set of all possible List where someClass is Number or is an ancestor of Number.
let say if wildcard is Object so line 2 wil be
`List<Object> sink = List<Number> nums `
In this context the ? doesn't get set arbitrarily, so this doesn't happen.
Lets have:
List<Integer> li = new ArrayList<>();
List<Object> lo = new ArrayList<>();
l = lo;//not allowed
lo.add("string");
Integer i = li.get(0);//that is string there
That is why you cannot make such assignment. I think compiler doesn't treat direct assignment of new ArrayList<Integer>() other then existing variable li.

Categories

Resources