DeQueueAny function not working as it's supposed to - java

So the prompt for this question is a popular one. It is as follows:
An animal shelter holds only dogs and cats, and operates on a strictly "first in, first out" basis. People must adopt either the "oldest" (based on arrival time) of all animals at the shelter, or they can select whether they would prefer a dog or a cat (and will receive the oldest animal of that type). They cannot select which specific animal they would like. Create the data structures to maintain this system and implement operations such as enqueue, dequeueAny, dequeueDog and dequeueCat.
For some reason, my dequeue any isn't working, specifically where I am comparing the date of the animal in the cat queue compared to the one in the dog queue. It only works when I start with adding a dog first in my array list but doesn't work when I add a cat first...
public static void main(String[] args) throws Exception {
AnimalShelter as = new AnimalShelter();
List<Animal> animals = Arrays.asList(new Cat(),new Dog(),new Cat(),new Dog(),new Cat(),
new Dog(),new Cat(),new Dog(),new Cat());
for (Animal animal : animals) {
as.enqueue(animal);
}
System.out.println(as.cats.peek().dateArrived.compareTo(as.dogs.peek().dateArrived));
as.dequeueAny();
System.out.println(as.cats.size());
System.out.println(as.dogs.size());
}
public Animal dequeueAny() throws Exception {
boolean noCat = cats.isEmpty();
boolean noDog = dogs.isEmpty();
if (noCat && noDog)
throw new Exception("There are no more animals!");
if (noCat)
return dogs.poll();
else if (noDog)
return cats.poll();
else
return cats.peek().dateArrived.compareTo(dogs.peek().dateArrived) < 0 ? cats.poll(): dogs.poll();`
}
For some reason the peek().compareTo() returns 0.

Related

Why does java fails trying to cast List<List<String>> to Collection<Collection<String>> [duplicate]

I'm a bit confused about how Java generics handle inheritance / polymorphism.
Assume the following hierarchy -
Animal (Parent)
Dog - Cat (Children)
So suppose I have a method doSomething(List<Animal> animals). By all the rules of inheritance and polymorphism, I would assume that a List<Dog> is a List<Animal> and a List<Cat> is a List<Animal> - and so either one could be passed to this method. Not so. If I want to achieve this behavior, I have to explicitly tell the method to accept a list of any subclass of Animal by saying doSomething(List<? extends Animal> animals).
I understand that this is Java's behavior. My question is why? Why is polymorphism generally implicit, but when it comes to generics it must be specified?
No, a List<Dog> is not a List<Animal>. Consider what you can do with a List<Animal> - you can add any animal to it... including a cat. Now, can you logically add a cat to a litter of puppies? Absolutely not.
// Illegal code - because otherwise life would be Bad
List<Dog> dogs = new ArrayList<Dog>(); // ArrayList implements List
List<Animal> animals = dogs; // Awooga awooga
animals.add(new Cat());
Dog dog = dogs.get(0); // This should be safe, right?
Suddenly you have a very confused cat.
Now, you can't add a Cat to a List<? extends Animal> because you don't know it's a List<Cat>. You can retrieve a value and know that it will be an Animal, but you can't add arbitrary animals. The reverse is true for List<? super Animal> - in that case you can add an Animal to it safely, but you don't know anything about what might be retrieved from it, because it could be a List<Object>.
What you are looking for is called covariant type parameters. This means that if one type of object can be substituted for another in a method (for instance, Animal can be replaced with Dog), the same applies to expressions using those objects (so List<Animal> could be replaced with List<Dog>). The problem is that covariance is not safe for mutable lists in general. Suppose you have a List<Dog>, and it is being used as a List<Animal>. What happens when you try to add a Cat to this List<Animal> which is really a List<Dog>? Automatically allowing type parameters to be covariant breaks the type system.
It would be useful to add syntax to allow type parameters to be specified as covariant, which avoids the ? extends Foo in method declarations, but that does add additional complexity.
The reason a List<Dog> is not a List<Animal>, is that, for example, you can insert a Cat into a List<Animal>, but not into a List<Dog>... you can use wildcards to make generics more extensible where possible; for example, reading from a List<Dog> is the similar to reading from a List<Animal> -- but not writing.
The Generics in the Java Language and the Section on Generics from the Java Tutorials have a very good, in-depth explanation as to why some things are or are not polymorphic or permitted with generics.
A point I think should be added to what other answers mention is that while
List<Dog> isn't-a List<Animal> in Java
it is also true that
A list of dogs is-a list of animals in English (under a reasonable interpretation)
The way the OP's intuition works - which is completely valid of course - is the latter sentence. However, if we apply this intuition we get a language that is not Java-esque in its type system: Suppose our language does allow adding a cat to our list of dogs. What would that mean? It would mean that the list ceases to be a list of dogs, and remains merely a list of animals. And a list of mammals, and a list of quadrapeds.
To put it another way: A List<Dog> in Java does not mean "a list of dogs" in English, it means "a list of dogs and nothing other than dogs".
More generally, OP's intuition lends itself towards a language in which operations on objects can change their type, or rather, an object's type(s) is a (dynamic) function of its value.
I would say the whole point of Generics is that it doesn't allow that. Consider the situation with arrays, which do allow that type of covariance:
Object[] objects = new String[10];
objects[0] = Boolean.FALSE;
That code compiles fine, but throws a runtime error (java.lang.ArrayStoreException: java.lang.Boolean in the second line). It is not typesafe. The point of Generics is to add the compile time type safety, otherwise you could just stick with a plain class without generics.
Now there are times where you need to be more flexible and that is what the ? super Class and ? extends Class are for. The former is when you need to insert into a type Collection (for example), and the latter is for when you need to read from it, in a type safe manner. But the only way to do both at the same time is to have a specific type.
To understand the problem it's useful to make comparison to arrays.
List<Dog> is not subclass of List<Animal>.
But Dog[] is subclass of Animal[].
Arrays are reifiable and covariant. Reifiable means their type information is fully available at runtime. Therefore arrays provide runtime type safety but not compile-time type safety.
// All compiles but throws ArrayStoreException at runtime at last line
Dog[] dogs = new Dog[10];
Animal[] animals = dogs; // compiles
animals[0] = new Cat(); // throws ArrayStoreException at runtime
It's vice versa for generics:
Generics are erased and invariant. Therefore generics can't provide runtime type safety, but they provide compile-time type safety. In the code below if generics were covariant it will be possible to make heap pollution at line 3.
List<Dog> dogs = new ArrayList<>();
List<Animal> animals = dogs; // compile-time error, otherwise heap pollution
animals.add(new Cat());
The answers given here didn't fully convince me. So instead, I make another example.
public void passOn(Consumer<Animal> consumer, Supplier<Animal> supplier) {
consumer.accept(supplier.get());
}
sounds fine, doesn't it? But you can only pass Consumers and Suppliers for Animals. If you have a Mammal consumer, but a Duck supplier, they should not fit although both are animals. In order to disallow this, additional restrictions have been added.
Instead of the above, we have to define relationships between the types we use.
E. g.,
public <A extends Animal> void passOn(Consumer<A> consumer, Supplier<? extends A> supplier) {
consumer.accept(supplier.get());
}
makes sure that we can only use a supplier which provides us the right type of object for the consumer.
OTOH, we could as well do
public <A extends Animal> void passOn(Consumer<? super A> consumer, Supplier<A> supplier) {
consumer.accept(supplier.get());
}
where we go the other way: we define the type of the Supplier and restrict that it can be put into the Consumer.
We even can do
public <A extends Animal> void passOn(Consumer<? super A> consumer, Supplier<? extends A> supplier) {
consumer.accept(supplier.get());
}
where, having the intuitive relations Life -> Animal -> Mammal -> Dog, Cat etc., we could even put a Mammal into a Life consumer, but not a String into a Life consumer.
The basis logic for such behavior is that Generics follow a mechanism of type erasure. So at run time you have no way if identifying the type of collection unlike arrays where there is no such erasure process. So coming back to your question...
So suppose there is a method as given below:
add(List<Animal>){
//You can add List<Dog or List<Cat> and this will compile as per rules of polymorphism
}
Now if java allows caller to add List of type Animal to this method then you might add wrong thing into collection and at run time too it will run due to type erasure. While in case of arrays you will get a run time exception for such scenarios...
Thus in essence this behavior is implemented so that one cannot add wrong thing into collection. Now I believe type erasure exists so as to give compatibility with legacy java without generics....
Others have done a decent job of explaining why you cannot just cast a list of descendant to list of superclass.
However, many people visit this question looking for a solution.
So, the solution to this problem since Java version 10 is as follows:
(Note: S = superclass)
List<S> supers = List.copyOf( descendants );
This function will do a cast if it is perfectly safe to do so, or a copy if a cast would not be safe.
For an in-depth explanation (which takes into consideration the potential pitfalls mentioned by other answers here) see related question and my 2022 answer to it: https://stackoverflow.com/a/72195980/773113
Subtyping is invariant for parameterized types. Even tough the class Dog is a subtype of Animal, the parameterized type List<Dog> is not a subtype of List<Animal>. In contrast, covariant subtyping is used by arrays, so the array
type Dog[] is a subtype of Animal[].
Invariant subtyping ensures that the type constraints enforced by Java are not violated. Consider the following code given by #Jon Skeet:
List<Dog> dogs = new ArrayList<Dog>(1);
List<Animal> animals = dogs;
animals.add(new Cat()); // compile-time error
Dog dog = dogs.get(0);
As stated by #Jon Skeet, this code is illegal, because otherwise it would violate the type constraints by returning a cat when a dog expected.
It is instructive to compare the above to analogous code for arrays.
Dog[] dogs = new Dog[1];
Object[] animals = dogs;
animals[0] = new Cat(); // run-time error
Dog dog = dogs[0];
The code is legal. However, throws an array store exception.
An array carries its type at run-time this way JVM can enforce
type safety of covariant subtyping.
To understand this further let's look at the bytecode generated by javap of the class below:
import java.util.ArrayList;
import java.util.List;
public class Demonstration {
public void normal() {
List normal = new ArrayList(1);
normal.add("lorem ipsum");
}
public void parameterized() {
List<String> parameterized = new ArrayList<>(1);
parameterized.add("lorem ipsum");
}
}
Using the command javap -c Demonstration, this shows the following Java bytecode:
Compiled from "Demonstration.java"
public class Demonstration {
public Demonstration();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public void normal();
Code:
0: new #2 // class java/util/ArrayList
3: dup
4: iconst_1
5: invokespecial #3 // Method java/util/ArrayList."<init>":(I)V
8: astore_1
9: aload_1
10: ldc #4 // String lorem ipsum
12: invokeinterface #5, 2 // InterfaceMethod java/util/List.add:(Ljava/lang/Object;)Z
17: pop
18: return
public void parameterized();
Code:
0: new #2 // class java/util/ArrayList
3: dup
4: iconst_1
5: invokespecial #3 // Method java/util/ArrayList."<init>":(I)V
8: astore_1
9: aload_1
10: ldc #4 // String lorem ipsum
12: invokeinterface #5, 2 // InterfaceMethod java/util/List.add:(Ljava/lang/Object;)Z
17: pop
18: return
}
Observe that the translated code of method bodies are identical. Compiler replaced each parameterized type by its erasure. This property is crucial meaning that it did not break backwards compatibility.
In conclusion, run-time safety is not possible for parameterized types, since compiler replaces each parameterized type by its erasure. This makes parameterized types are nothing more than syntactic sugar.
If you are sure that the list items are subclasses of that given super type, you can cast the list using this approach:
(List<Animal>) (List<?>) dogs
This is usefull when you want to pass the list inside of a constructor or iterate over it.
The answer as well as other answers are correct. I am going to add to those answers with a solution that I think will be helpful. I think this comes up often in programming. One thing to note is that for Collections (Lists, Sets, etc.) the main issue is adding to the Collection. That is where things break down. Even removing is OK.
In most cases, we can use Collection<? extends T> rather then Collection<T> and that should be the first choice. However, I am finding cases where it is not easy to do that. It is up for debate as to whether that is always the best thing to do. I am presenting here a class DownCastCollection that can take convert a Collection<? extends T> to a Collection<T> (we can define similar classes for List, Set, NavigableSet,..) to be used when using the standard approach is very inconvenient. Below is an example of how to use it (we could also use Collection<? extends Object> in this case, but I am keeping it simple to illustrate using DownCastCollection.
/**Could use Collection<? extends Object> and that is the better choice.
* But I am doing this to illustrate how to use DownCastCollection. **/
public static void print(Collection<Object> col){
for(Object obj : col){
System.out.println(obj);
}
}
public static void main(String[] args){
ArrayList<String> list = new ArrayList<>();
list.addAll(Arrays.asList("a","b","c"));
print(new DownCastCollection<Object>(list));
}
Now the class:
import java.util.AbstractCollection;
import java.util.Collection;
import java.util.Iterator;
import java.util.NoSuchElementException;
public class DownCastCollection<E> extends AbstractCollection<E> implements Collection<E> {
private Collection<? extends E> delegate;
public DownCastCollection(Collection<? extends E> delegate) {
super();
this.delegate = delegate;
}
#Override
public int size() {
return delegate ==null ? 0 : delegate.size();
}
#Override
public boolean isEmpty() {
return delegate==null || delegate.isEmpty();
}
#Override
public boolean contains(Object o) {
if(isEmpty()) return false;
return delegate.contains(o);
}
private class MyIterator implements Iterator<E>{
Iterator<? extends E> delegateIterator;
protected MyIterator() {
super();
this.delegateIterator = delegate == null ? null :delegate.iterator();
}
#Override
public boolean hasNext() {
return delegateIterator != null && delegateIterator.hasNext();
}
#Override
public E next() {
if(!hasNext()) throw new NoSuchElementException("The iterator is empty");
return delegateIterator.next();
}
#Override
public void remove() {
delegateIterator.remove();
}
}
#Override
public Iterator<E> iterator() {
return new MyIterator();
}
#Override
public boolean add(E e) {
throw new UnsupportedOperationException();
}
#Override
public boolean remove(Object o) {
if(delegate == null) return false;
return delegate.remove(o);
}
#Override
public boolean containsAll(Collection<?> c) {
if(delegate==null) return false;
return delegate.containsAll(c);
}
#Override
public boolean addAll(Collection<? extends E> c) {
throw new UnsupportedOperationException();
}
#Override
public boolean removeAll(Collection<?> c) {
if(delegate == null) return false;
return delegate.removeAll(c);
}
#Override
public boolean retainAll(Collection<?> c) {
if(delegate == null) return false;
return delegate.retainAll(c);
}
#Override
public void clear() {
if(delegate == null) return;
delegate.clear();
}
}
The issue has been correctly identified as related to variance but the details are not correct. A purely functional list is a covariant data functor, which means if a type Sub is a subtype of Super, then a list of Sub is definitely a subtype of a list of Super.
However mutability of a list is not the basic problem here. The problem is mutability in general. The problem is well known, and is called the Covariance Problem, it was first identified I think by Castagna, and it completely and utterly destroys object orientation as a general paradigm. It is based on previously established variance rules established by Cardelli and Reynolds.
Somewhat oversimplifying, lets consider assignment of an object B of type T to an object A of type T as a mutation. This is without loss of generality: a mutation of A can be written A = f (A) where f: T -> T. The problem, of course, is that whilst functions are covariant in their codomain, they're contravariant in their domain, but with assignments the domain and codomain are the same, so assignment is invariant!
It follows, generalising, that subtypes cannot be mutated. But with object orientation mutation is fundamental, hence object orientation is intrinsically flawed.
Here's a simple example: in a purely functional setting a symmetric matrix is clearly a matrix, it is a subtype, no problem. Now lets add to matrix the ability to set a single element at coordinates (x,y) with the rule no other element changes. Now symmetric matrix is no longer a subtype, if you change (x,y) you have also changed (y,x). The functional operation is delta: Sym -> Mat, if you change one element of a symmetric matrix you get a general non-symmetric matrix back. Therefore if you included a "change one element" method in Mat, Sym is not a subtype. In fact .. there are almost certainly NO proper subtypes.
To put all this in easier terms: if you have a general data type with a wide range of mutators which leverage its generality you can be certain any proper subtype cannot possibly support all those mutations: if it could, it would be just as general as the supertype, contrary to the specification of "proper" subtype.
The fact Java prevents subtyping mutable lists fails to address the real issue: why are you using object oriented rubbish like Java when it was discredited several decades ago??
In any case there's a reasonable discussion here:
https://en.wikipedia.org/wiki/Covariance_and_contravariance_(computer_science)
Lets take the example from JavaSE tutorial
public abstract class Shape {
public abstract void draw(Canvas c);
}
public class Circle extends Shape {
private int x, y, radius;
public void draw(Canvas c) {
...
}
}
public class Rectangle extends Shape {
private int x, y, width, height;
public void draw(Canvas c) {
...
}
}
So why a list of dogs (circles) should not be considered implicitly a list of animals (shapes) is because of this situation:
// drawAll method call
drawAll(circleList);
public void drawAll(List<Shape> shapes) {
shapes.add(new Rectangle());
}
So Java "architects" had 2 options which address this problem:
do not consider that a subtype is implicitly it's supertype, and give a compile error, like it happens now
consider the subtype to be it's supertype and restrict at compile the "add" method (so in the drawAll method, if a list of circles, subtype of shape, would be passed, the compiler should detected that and restrict you with compile error into doing that).
For obvious reasons, that chose the first way.
We should also take in consideration how the compiler threats the generic classes: in "instantiates" a different type whenever we fill the generic arguments.
Thus we have ListOfAnimal, ListOfDog, ListOfCat, etc, which are distinct classes that end up being "created" by the compiler when we specify the generic arguments. And this is a flat hierarchy (actually regarding to List is not a hierarchy at all).
Another argument why covariance doesn't make sense in case of generic classes is the fact that at base all classes are the same - are List instances. Specialising a List by filling the generic argument doesn't extend the class, it just makes it work for that particular generic argument.
The problem has been well-identified. But there's a solution; make doSomething generic:
<T extends Animal> void doSomething<List<T> animals) {
}
now you can call doSomething with either List<Dog> or List<Cat> or List<Animal>.
another solution is to build a new list
List<Dog> dogs = new ArrayList<Dog>();
List<Animal> animals = new ArrayList<Animal>(dogs);
animals.add(new Cat());
Further to the answer by Jon Skeet, which uses this example code:
// Illegal code - because otherwise life would be Bad
List<Dog> dogs = new ArrayList<Dog>(); // ArrayList implements List
List<Animal> animals = dogs; // Awooga awooga
animals.add(new Cat());
Dog dog = dogs.get(0); // This should be safe, right?
At the deepest level, the problem here is that dogs and animals share a reference. That means that one way to make this work would be to copy the entire list, which would break reference equality:
// This code is fine
List<Dog> dogs = new ArrayList<Dog>();
dogs.add(new Dog());
List<Animal> animals = new ArrayList<>(dogs); // Copy list
animals.add(new Cat());
Dog dog = dogs.get(0); // This is fine now, because it does not return the Cat
After calling List<Animal> animals = new ArrayList<>(dogs);, you cannot subsequently directly assign animals to either dogs or cats:
// These are both illegal
dogs = animals;
cats = animals;
therefore you can't put the wrong subtype of Animal into the list, because there is no wrong subtype -- any object of subtype ? extends Animal can be added to animals.
Obviously, this changes the semantics, since the lists animals and dogs are no longer shared, so adding to one list does not add to the other (which is exactly what you want, to avoid the problem that a Cat could be added to a list that is only supposed to contain Dog objects). Also, copying the entire list can be inefficient. However, this does solve the type equivalence problem, by breaking reference equality.
I see that the question has already been answered a number of times, just want to put in my inputs on the same question.
Lets us go ahead and create a simplified Animal class hierarchy.
abstract class Animal {
void eat() {
System.out.println("animal eating");
}
}
class Dog extends Animal {
void bark() { }
}
class Cat extends Animal {
void meow() { }
}
Now let us have a look at our old friend Arrays, which we know support polymorphism implicitly-
class TestAnimals {
public static void main(String[] args) {
Animal[] animals = {new Dog(), new Cat(), new Dog()};
Dog[] dogs = {new Dog(), new Dog(), new Dog()};
takeAnimals(animals);
takeAnimals(dogs);
}
public void takeAnimals(Animal[] animals) {
for(Animal a : animals) {
System.out.println(a.eat());
}
}
}
The class compiles fine and when we run the above class we get the output
animal eating
animal eating
animal eating
animal eating
animal eating
animal eating
The point to note here is that the takeAnimals() method is defined to take anything which is of type Animal, it can take an array of type Animal and it can take an array of Dog as well because Dog-is-a-Animal. So this is Polymorphism in action.
Let us now use this same approach with generics,
Now say we tweak our code a little bit and use ArrayLists instead of Arrays -
class TestAnimals {
public static void main(String[] args) {
ArrayList<Animal> animals = new ArrayList<Animal>();
animals.add(new Dog());
animals.add(new Cat());
animals.add(new Dog());
takeAnimals(animals);
}
public void takeAnimals(ArrayList<Animal> animals) {
for(Animal a : animals) {
System.out.println(a.eat());
}
}
}
The class above will compile and will produce the output -
animal eating
animal eating
animal eating
animal eating
animal eating
animal eating
So we know this works, now lets tweak this class a little bit to use Animal type polymorphically -
class TestAnimals {
public static void main(String[] args) {
ArrayList<Animal> animals = new ArrayList<Animal>();
animals.add(new Dog());
animals.add(new Cat());
animals.add(new Dog());
ArrayList<Dog> dogs = new ArrayList<Dog>();
takeAnimals(animals);
takeAnimals(dogs);
}
public void takeAnimals(ArrayList<Animal> animals) {
for(Animal a : animals) {
System.out.println(a.eat());
}
}
}
Looks like there should be no problem in compiling the above class as the takeAnimals() method is designed to take any ArrayList of type Animal and Dog-is-a-Animal so it should not be a deal breaker here.
But, unfortunately the compiler throws an error and doesn't allow us to pass a Dog ArrayList to a variable expecting Animal ArrayList.
You ask why?
Because just imagine, if JAVA were to allow the Dog ArrayList - dogs - to be put into the Animal ArrayList - animals - and then inside the takeAnimals() method somebody does something like -
animals.add(new Cat());
thinking that this should be doable because ideally it is an Animal ArrayList and you should be in a position to add any cat to it as Cat-is-also-a-Animal, but in real you passed a Dog type ArrayList to it.
So, now you must be thinking the the same should have happened with the Arrays as well. You are right in thinking so.
If somebody tries to do the same thing with Arrays then Arrays are also going to throw an error but Arrays handle this error at runtime whereas ArrayLists handle this error at compile time.

method chaining, returning an object from a list and getting values from it

what I have is a list of Dog objects, in the objects there contains a value that is a Boolean to show whether the dog as completed a training or not. What im trying to do is iterate over the list and only return Dog() objects that have completed training, for instance if their are 12 dogs, and only 3 have completed training, the loop should only print those objects.
else if (input == 1) {
for (int i = 0; i < 12; ++i) {
//Create a temporary value to hold the object.
Object tempHold = dogKennel.getAnimal(i);
//If animal has not graduated, skip, else print.
if (!(tempHold.getGraduation())) {
continue;
}
else {
System.out.println(dogKennel);
}
}
getAnimal(i) returns the object at int i
the method .getGraduation is defined and does return a Boolean however the compiler doesnt want to recognize temp value as is, and doesnt go beyond that value. the compiler keeps suggesting to cast tempHold, but even if I do, it doesnt work.
i feel like it would work if i could get it to compile, as the object that is returned would have a getGraduation() method (it is defined for the super class of the animal.)
however the compiler doesn't want to recognize temp value as is, ...the compiler keeps suggesting to cast tempHold, but even if I do, it doesn't work.
The compiler is telling you two things:
An Object is NOT a Dog (the opposite is true. A Dog is an Object).
class Object does not have method isGraduated() defined for them.
To fix this, you can cast Object to Dog:
Dog tempHold = (Dog)dogKennel.getAnimal(i);
Now that we have a Dog, we can safely invoke isGraduated() on it. But the problem is we canNOT be sure that we have a Dog. We may as well have a Cat if we get the Animal from another kennel. In that case, you will get a ClassCastException which tells you that Cats cannot be cast as Dogs.
To avoid getting run time exceptions, you can add a check:
Object tempHold = dogKennel.getAnimal(i);
if(tempHold instanceof Dog) {
Dog dog = (Dog)tempHold;
System.out.println(dog.isGraduated());
}
The instanceof check fixes the problem.
There are ways to avoid this run time check altogether. One would be to create an interface:
public interface CanGraduate {
default boolean isGraduated() {
return false;
};
}
Then make all Animals implement this interface:
public abstract class Animal implements CanGraduate {
//Behavior common among all animals
}
You can now freely add new animal types and be assured that you can safely invoke isGraduated() on them and get a false value as long as they inherit from the above Animal class.
For dogs, isGraduated() is supposed to be more meaningful. So you can override it in their case:
public class Dog extends Animal {
private boolean _graduated = true;
#Override
public boolean isGraduated() {
return _graduated; //or some complex logic that determines graduation
}
}
With this structure, you no longer need to worry about invoking the method on any kind of Animal.
As an example, let us see some driver code:
public class Main {
public static void main(String[] args) {
Dog dog1 = new Dog();
Dog dog2 = new Dog();
Cat cat1 = new Cat();
Cat cat2 = new Cat();
List<Animal> dogKennel = List.of(dog1, dog2);
List<Animal> catKennel = List.of(cat1, cat2);
for(Animal x : catKennel) {
System.out.println(x.isGraduated());
}
}
}
The program will simply output false since Cats can never graduate. If the kennel contained Dogs, it would output the actual graduation status of the dog.
Like the others have said, the getGraduation() method is only defined presumably in the Dog class. This means that the method can only be called on Objects with the type Dog. To define a variable with type Dog you can do Dog temphold = *whatever*. The reason it wants you to cast is because Object is a supertype of Dog. If you'd like to read more about casting you can here: https://javarevisited.blogspot.com/2012/12/what-is-type-casting-in-java-class-interface-example.html
Basically, all you have to do is a cast to convert the object
if (!(((Dog)tempHold).getGraduation()))
{
continue;
}
this casting tells the compiler that even though tempHold is an object of the Object class it also is an object of the Dog class and should have all of its properties
If you want to filter the list for just ones with a certain condition, the most common way to do that these days is with the Stream::filter method.
It would look like this:
List<Dog> completedTraining =
dogKennel
.stream()
.filter(
dog -> !dog.getGraduation()
)
.collect(
Collectors.toList()
)
;

Why List<Number> is NOT equal to List<Integer> - How this design is useful? [duplicate]

I'm a bit confused about how Java generics handle inheritance / polymorphism.
Assume the following hierarchy -
Animal (Parent)
Dog - Cat (Children)
So suppose I have a method doSomething(List<Animal> animals). By all the rules of inheritance and polymorphism, I would assume that a List<Dog> is a List<Animal> and a List<Cat> is a List<Animal> - and so either one could be passed to this method. Not so. If I want to achieve this behavior, I have to explicitly tell the method to accept a list of any subclass of Animal by saying doSomething(List<? extends Animal> animals).
I understand that this is Java's behavior. My question is why? Why is polymorphism generally implicit, but when it comes to generics it must be specified?
No, a List<Dog> is not a List<Animal>. Consider what you can do with a List<Animal> - you can add any animal to it... including a cat. Now, can you logically add a cat to a litter of puppies? Absolutely not.
// Illegal code - because otherwise life would be Bad
List<Dog> dogs = new ArrayList<Dog>(); // ArrayList implements List
List<Animal> animals = dogs; // Awooga awooga
animals.add(new Cat());
Dog dog = dogs.get(0); // This should be safe, right?
Suddenly you have a very confused cat.
Now, you can't add a Cat to a List<? extends Animal> because you don't know it's a List<Cat>. You can retrieve a value and know that it will be an Animal, but you can't add arbitrary animals. The reverse is true for List<? super Animal> - in that case you can add an Animal to it safely, but you don't know anything about what might be retrieved from it, because it could be a List<Object>.
What you are looking for is called covariant type parameters. This means that if one type of object can be substituted for another in a method (for instance, Animal can be replaced with Dog), the same applies to expressions using those objects (so List<Animal> could be replaced with List<Dog>). The problem is that covariance is not safe for mutable lists in general. Suppose you have a List<Dog>, and it is being used as a List<Animal>. What happens when you try to add a Cat to this List<Animal> which is really a List<Dog>? Automatically allowing type parameters to be covariant breaks the type system.
It would be useful to add syntax to allow type parameters to be specified as covariant, which avoids the ? extends Foo in method declarations, but that does add additional complexity.
The reason a List<Dog> is not a List<Animal>, is that, for example, you can insert a Cat into a List<Animal>, but not into a List<Dog>... you can use wildcards to make generics more extensible where possible; for example, reading from a List<Dog> is the similar to reading from a List<Animal> -- but not writing.
The Generics in the Java Language and the Section on Generics from the Java Tutorials have a very good, in-depth explanation as to why some things are or are not polymorphic or permitted with generics.
A point I think should be added to what other answers mention is that while
List<Dog> isn't-a List<Animal> in Java
it is also true that
A list of dogs is-a list of animals in English (under a reasonable interpretation)
The way the OP's intuition works - which is completely valid of course - is the latter sentence. However, if we apply this intuition we get a language that is not Java-esque in its type system: Suppose our language does allow adding a cat to our list of dogs. What would that mean? It would mean that the list ceases to be a list of dogs, and remains merely a list of animals. And a list of mammals, and a list of quadrapeds.
To put it another way: A List<Dog> in Java does not mean "a list of dogs" in English, it means "a list of dogs and nothing other than dogs".
More generally, OP's intuition lends itself towards a language in which operations on objects can change their type, or rather, an object's type(s) is a (dynamic) function of its value.
I would say the whole point of Generics is that it doesn't allow that. Consider the situation with arrays, which do allow that type of covariance:
Object[] objects = new String[10];
objects[0] = Boolean.FALSE;
That code compiles fine, but throws a runtime error (java.lang.ArrayStoreException: java.lang.Boolean in the second line). It is not typesafe. The point of Generics is to add the compile time type safety, otherwise you could just stick with a plain class without generics.
Now there are times where you need to be more flexible and that is what the ? super Class and ? extends Class are for. The former is when you need to insert into a type Collection (for example), and the latter is for when you need to read from it, in a type safe manner. But the only way to do both at the same time is to have a specific type.
To understand the problem it's useful to make comparison to arrays.
List<Dog> is not subclass of List<Animal>.
But Dog[] is subclass of Animal[].
Arrays are reifiable and covariant. Reifiable means their type information is fully available at runtime. Therefore arrays provide runtime type safety but not compile-time type safety.
// All compiles but throws ArrayStoreException at runtime at last line
Dog[] dogs = new Dog[10];
Animal[] animals = dogs; // compiles
animals[0] = new Cat(); // throws ArrayStoreException at runtime
It's vice versa for generics:
Generics are erased and invariant. Therefore generics can't provide runtime type safety, but they provide compile-time type safety. In the code below if generics were covariant it will be possible to make heap pollution at line 3.
List<Dog> dogs = new ArrayList<>();
List<Animal> animals = dogs; // compile-time error, otherwise heap pollution
animals.add(new Cat());
The answers given here didn't fully convince me. So instead, I make another example.
public void passOn(Consumer<Animal> consumer, Supplier<Animal> supplier) {
consumer.accept(supplier.get());
}
sounds fine, doesn't it? But you can only pass Consumers and Suppliers for Animals. If you have a Mammal consumer, but a Duck supplier, they should not fit although both are animals. In order to disallow this, additional restrictions have been added.
Instead of the above, we have to define relationships between the types we use.
E. g.,
public <A extends Animal> void passOn(Consumer<A> consumer, Supplier<? extends A> supplier) {
consumer.accept(supplier.get());
}
makes sure that we can only use a supplier which provides us the right type of object for the consumer.
OTOH, we could as well do
public <A extends Animal> void passOn(Consumer<? super A> consumer, Supplier<A> supplier) {
consumer.accept(supplier.get());
}
where we go the other way: we define the type of the Supplier and restrict that it can be put into the Consumer.
We even can do
public <A extends Animal> void passOn(Consumer<? super A> consumer, Supplier<? extends A> supplier) {
consumer.accept(supplier.get());
}
where, having the intuitive relations Life -> Animal -> Mammal -> Dog, Cat etc., we could even put a Mammal into a Life consumer, but not a String into a Life consumer.
The basis logic for such behavior is that Generics follow a mechanism of type erasure. So at run time you have no way if identifying the type of collection unlike arrays where there is no such erasure process. So coming back to your question...
So suppose there is a method as given below:
add(List<Animal>){
//You can add List<Dog or List<Cat> and this will compile as per rules of polymorphism
}
Now if java allows caller to add List of type Animal to this method then you might add wrong thing into collection and at run time too it will run due to type erasure. While in case of arrays you will get a run time exception for such scenarios...
Thus in essence this behavior is implemented so that one cannot add wrong thing into collection. Now I believe type erasure exists so as to give compatibility with legacy java without generics....
Others have done a decent job of explaining why you cannot just cast a list of descendant to list of superclass.
However, many people visit this question looking for a solution.
So, the solution to this problem since Java version 10 is as follows:
(Note: S = superclass)
List<S> supers = List.copyOf( descendants );
This function will do a cast if it is perfectly safe to do so, or a copy if a cast would not be safe.
For an in-depth explanation (which takes into consideration the potential pitfalls mentioned by other answers here) see related question and my 2022 answer to it: https://stackoverflow.com/a/72195980/773113
Subtyping is invariant for parameterized types. Even tough the class Dog is a subtype of Animal, the parameterized type List<Dog> is not a subtype of List<Animal>. In contrast, covariant subtyping is used by arrays, so the array
type Dog[] is a subtype of Animal[].
Invariant subtyping ensures that the type constraints enforced by Java are not violated. Consider the following code given by #Jon Skeet:
List<Dog> dogs = new ArrayList<Dog>(1);
List<Animal> animals = dogs;
animals.add(new Cat()); // compile-time error
Dog dog = dogs.get(0);
As stated by #Jon Skeet, this code is illegal, because otherwise it would violate the type constraints by returning a cat when a dog expected.
It is instructive to compare the above to analogous code for arrays.
Dog[] dogs = new Dog[1];
Object[] animals = dogs;
animals[0] = new Cat(); // run-time error
Dog dog = dogs[0];
The code is legal. However, throws an array store exception.
An array carries its type at run-time this way JVM can enforce
type safety of covariant subtyping.
To understand this further let's look at the bytecode generated by javap of the class below:
import java.util.ArrayList;
import java.util.List;
public class Demonstration {
public void normal() {
List normal = new ArrayList(1);
normal.add("lorem ipsum");
}
public void parameterized() {
List<String> parameterized = new ArrayList<>(1);
parameterized.add("lorem ipsum");
}
}
Using the command javap -c Demonstration, this shows the following Java bytecode:
Compiled from "Demonstration.java"
public class Demonstration {
public Demonstration();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public void normal();
Code:
0: new #2 // class java/util/ArrayList
3: dup
4: iconst_1
5: invokespecial #3 // Method java/util/ArrayList."<init>":(I)V
8: astore_1
9: aload_1
10: ldc #4 // String lorem ipsum
12: invokeinterface #5, 2 // InterfaceMethod java/util/List.add:(Ljava/lang/Object;)Z
17: pop
18: return
public void parameterized();
Code:
0: new #2 // class java/util/ArrayList
3: dup
4: iconst_1
5: invokespecial #3 // Method java/util/ArrayList."<init>":(I)V
8: astore_1
9: aload_1
10: ldc #4 // String lorem ipsum
12: invokeinterface #5, 2 // InterfaceMethod java/util/List.add:(Ljava/lang/Object;)Z
17: pop
18: return
}
Observe that the translated code of method bodies are identical. Compiler replaced each parameterized type by its erasure. This property is crucial meaning that it did not break backwards compatibility.
In conclusion, run-time safety is not possible for parameterized types, since compiler replaces each parameterized type by its erasure. This makes parameterized types are nothing more than syntactic sugar.
If you are sure that the list items are subclasses of that given super type, you can cast the list using this approach:
(List<Animal>) (List<?>) dogs
This is usefull when you want to pass the list inside of a constructor or iterate over it.
The answer as well as other answers are correct. I am going to add to those answers with a solution that I think will be helpful. I think this comes up often in programming. One thing to note is that for Collections (Lists, Sets, etc.) the main issue is adding to the Collection. That is where things break down. Even removing is OK.
In most cases, we can use Collection<? extends T> rather then Collection<T> and that should be the first choice. However, I am finding cases where it is not easy to do that. It is up for debate as to whether that is always the best thing to do. I am presenting here a class DownCastCollection that can take convert a Collection<? extends T> to a Collection<T> (we can define similar classes for List, Set, NavigableSet,..) to be used when using the standard approach is very inconvenient. Below is an example of how to use it (we could also use Collection<? extends Object> in this case, but I am keeping it simple to illustrate using DownCastCollection.
/**Could use Collection<? extends Object> and that is the better choice.
* But I am doing this to illustrate how to use DownCastCollection. **/
public static void print(Collection<Object> col){
for(Object obj : col){
System.out.println(obj);
}
}
public static void main(String[] args){
ArrayList<String> list = new ArrayList<>();
list.addAll(Arrays.asList("a","b","c"));
print(new DownCastCollection<Object>(list));
}
Now the class:
import java.util.AbstractCollection;
import java.util.Collection;
import java.util.Iterator;
import java.util.NoSuchElementException;
public class DownCastCollection<E> extends AbstractCollection<E> implements Collection<E> {
private Collection<? extends E> delegate;
public DownCastCollection(Collection<? extends E> delegate) {
super();
this.delegate = delegate;
}
#Override
public int size() {
return delegate ==null ? 0 : delegate.size();
}
#Override
public boolean isEmpty() {
return delegate==null || delegate.isEmpty();
}
#Override
public boolean contains(Object o) {
if(isEmpty()) return false;
return delegate.contains(o);
}
private class MyIterator implements Iterator<E>{
Iterator<? extends E> delegateIterator;
protected MyIterator() {
super();
this.delegateIterator = delegate == null ? null :delegate.iterator();
}
#Override
public boolean hasNext() {
return delegateIterator != null && delegateIterator.hasNext();
}
#Override
public E next() {
if(!hasNext()) throw new NoSuchElementException("The iterator is empty");
return delegateIterator.next();
}
#Override
public void remove() {
delegateIterator.remove();
}
}
#Override
public Iterator<E> iterator() {
return new MyIterator();
}
#Override
public boolean add(E e) {
throw new UnsupportedOperationException();
}
#Override
public boolean remove(Object o) {
if(delegate == null) return false;
return delegate.remove(o);
}
#Override
public boolean containsAll(Collection<?> c) {
if(delegate==null) return false;
return delegate.containsAll(c);
}
#Override
public boolean addAll(Collection<? extends E> c) {
throw new UnsupportedOperationException();
}
#Override
public boolean removeAll(Collection<?> c) {
if(delegate == null) return false;
return delegate.removeAll(c);
}
#Override
public boolean retainAll(Collection<?> c) {
if(delegate == null) return false;
return delegate.retainAll(c);
}
#Override
public void clear() {
if(delegate == null) return;
delegate.clear();
}
}
The issue has been correctly identified as related to variance but the details are not correct. A purely functional list is a covariant data functor, which means if a type Sub is a subtype of Super, then a list of Sub is definitely a subtype of a list of Super.
However mutability of a list is not the basic problem here. The problem is mutability in general. The problem is well known, and is called the Covariance Problem, it was first identified I think by Castagna, and it completely and utterly destroys object orientation as a general paradigm. It is based on previously established variance rules established by Cardelli and Reynolds.
Somewhat oversimplifying, lets consider assignment of an object B of type T to an object A of type T as a mutation. This is without loss of generality: a mutation of A can be written A = f (A) where f: T -> T. The problem, of course, is that whilst functions are covariant in their codomain, they're contravariant in their domain, but with assignments the domain and codomain are the same, so assignment is invariant!
It follows, generalising, that subtypes cannot be mutated. But with object orientation mutation is fundamental, hence object orientation is intrinsically flawed.
Here's a simple example: in a purely functional setting a symmetric matrix is clearly a matrix, it is a subtype, no problem. Now lets add to matrix the ability to set a single element at coordinates (x,y) with the rule no other element changes. Now symmetric matrix is no longer a subtype, if you change (x,y) you have also changed (y,x). The functional operation is delta: Sym -> Mat, if you change one element of a symmetric matrix you get a general non-symmetric matrix back. Therefore if you included a "change one element" method in Mat, Sym is not a subtype. In fact .. there are almost certainly NO proper subtypes.
To put all this in easier terms: if you have a general data type with a wide range of mutators which leverage its generality you can be certain any proper subtype cannot possibly support all those mutations: if it could, it would be just as general as the supertype, contrary to the specification of "proper" subtype.
The fact Java prevents subtyping mutable lists fails to address the real issue: why are you using object oriented rubbish like Java when it was discredited several decades ago??
In any case there's a reasonable discussion here:
https://en.wikipedia.org/wiki/Covariance_and_contravariance_(computer_science)
Lets take the example from JavaSE tutorial
public abstract class Shape {
public abstract void draw(Canvas c);
}
public class Circle extends Shape {
private int x, y, radius;
public void draw(Canvas c) {
...
}
}
public class Rectangle extends Shape {
private int x, y, width, height;
public void draw(Canvas c) {
...
}
}
So why a list of dogs (circles) should not be considered implicitly a list of animals (shapes) is because of this situation:
// drawAll method call
drawAll(circleList);
public void drawAll(List<Shape> shapes) {
shapes.add(new Rectangle());
}
So Java "architects" had 2 options which address this problem:
do not consider that a subtype is implicitly it's supertype, and give a compile error, like it happens now
consider the subtype to be it's supertype and restrict at compile the "add" method (so in the drawAll method, if a list of circles, subtype of shape, would be passed, the compiler should detected that and restrict you with compile error into doing that).
For obvious reasons, that chose the first way.
We should also take in consideration how the compiler threats the generic classes: in "instantiates" a different type whenever we fill the generic arguments.
Thus we have ListOfAnimal, ListOfDog, ListOfCat, etc, which are distinct classes that end up being "created" by the compiler when we specify the generic arguments. And this is a flat hierarchy (actually regarding to List is not a hierarchy at all).
Another argument why covariance doesn't make sense in case of generic classes is the fact that at base all classes are the same - are List instances. Specialising a List by filling the generic argument doesn't extend the class, it just makes it work for that particular generic argument.
The problem has been well-identified. But there's a solution; make doSomething generic:
<T extends Animal> void doSomething<List<T> animals) {
}
now you can call doSomething with either List<Dog> or List<Cat> or List<Animal>.
another solution is to build a new list
List<Dog> dogs = new ArrayList<Dog>();
List<Animal> animals = new ArrayList<Animal>(dogs);
animals.add(new Cat());
Further to the answer by Jon Skeet, which uses this example code:
// Illegal code - because otherwise life would be Bad
List<Dog> dogs = new ArrayList<Dog>(); // ArrayList implements List
List<Animal> animals = dogs; // Awooga awooga
animals.add(new Cat());
Dog dog = dogs.get(0); // This should be safe, right?
At the deepest level, the problem here is that dogs and animals share a reference. That means that one way to make this work would be to copy the entire list, which would break reference equality:
// This code is fine
List<Dog> dogs = new ArrayList<Dog>();
dogs.add(new Dog());
List<Animal> animals = new ArrayList<>(dogs); // Copy list
animals.add(new Cat());
Dog dog = dogs.get(0); // This is fine now, because it does not return the Cat
After calling List<Animal> animals = new ArrayList<>(dogs);, you cannot subsequently directly assign animals to either dogs or cats:
// These are both illegal
dogs = animals;
cats = animals;
therefore you can't put the wrong subtype of Animal into the list, because there is no wrong subtype -- any object of subtype ? extends Animal can be added to animals.
Obviously, this changes the semantics, since the lists animals and dogs are no longer shared, so adding to one list does not add to the other (which is exactly what you want, to avoid the problem that a Cat could be added to a list that is only supposed to contain Dog objects). Also, copying the entire list can be inefficient. However, this does solve the type equivalence problem, by breaking reference equality.
I see that the question has already been answered a number of times, just want to put in my inputs on the same question.
Lets us go ahead and create a simplified Animal class hierarchy.
abstract class Animal {
void eat() {
System.out.println("animal eating");
}
}
class Dog extends Animal {
void bark() { }
}
class Cat extends Animal {
void meow() { }
}
Now let us have a look at our old friend Arrays, which we know support polymorphism implicitly-
class TestAnimals {
public static void main(String[] args) {
Animal[] animals = {new Dog(), new Cat(), new Dog()};
Dog[] dogs = {new Dog(), new Dog(), new Dog()};
takeAnimals(animals);
takeAnimals(dogs);
}
public void takeAnimals(Animal[] animals) {
for(Animal a : animals) {
System.out.println(a.eat());
}
}
}
The class compiles fine and when we run the above class we get the output
animal eating
animal eating
animal eating
animal eating
animal eating
animal eating
The point to note here is that the takeAnimals() method is defined to take anything which is of type Animal, it can take an array of type Animal and it can take an array of Dog as well because Dog-is-a-Animal. So this is Polymorphism in action.
Let us now use this same approach with generics,
Now say we tweak our code a little bit and use ArrayLists instead of Arrays -
class TestAnimals {
public static void main(String[] args) {
ArrayList<Animal> animals = new ArrayList<Animal>();
animals.add(new Dog());
animals.add(new Cat());
animals.add(new Dog());
takeAnimals(animals);
}
public void takeAnimals(ArrayList<Animal> animals) {
for(Animal a : animals) {
System.out.println(a.eat());
}
}
}
The class above will compile and will produce the output -
animal eating
animal eating
animal eating
animal eating
animal eating
animal eating
So we know this works, now lets tweak this class a little bit to use Animal type polymorphically -
class TestAnimals {
public static void main(String[] args) {
ArrayList<Animal> animals = new ArrayList<Animal>();
animals.add(new Dog());
animals.add(new Cat());
animals.add(new Dog());
ArrayList<Dog> dogs = new ArrayList<Dog>();
takeAnimals(animals);
takeAnimals(dogs);
}
public void takeAnimals(ArrayList<Animal> animals) {
for(Animal a : animals) {
System.out.println(a.eat());
}
}
}
Looks like there should be no problem in compiling the above class as the takeAnimals() method is designed to take any ArrayList of type Animal and Dog-is-a-Animal so it should not be a deal breaker here.
But, unfortunately the compiler throws an error and doesn't allow us to pass a Dog ArrayList to a variable expecting Animal ArrayList.
You ask why?
Because just imagine, if JAVA were to allow the Dog ArrayList - dogs - to be put into the Animal ArrayList - animals - and then inside the takeAnimals() method somebody does something like -
animals.add(new Cat());
thinking that this should be doable because ideally it is an Animal ArrayList and you should be in a position to add any cat to it as Cat-is-also-a-Animal, but in real you passed a Dog type ArrayList to it.
So, now you must be thinking the the same should have happened with the Arrays as well. You are right in thinking so.
If somebody tries to do the same thing with Arrays then Arrays are also going to throw an error but Arrays handle this error at runtime whereas ArrayLists handle this error at compile time.

How to instantiate nested Lists in java? list<list<Integer>> [duplicate]

I'm a bit confused about how Java generics handle inheritance / polymorphism.
Assume the following hierarchy -
Animal (Parent)
Dog - Cat (Children)
So suppose I have a method doSomething(List<Animal> animals). By all the rules of inheritance and polymorphism, I would assume that a List<Dog> is a List<Animal> and a List<Cat> is a List<Animal> - and so either one could be passed to this method. Not so. If I want to achieve this behavior, I have to explicitly tell the method to accept a list of any subclass of Animal by saying doSomething(List<? extends Animal> animals).
I understand that this is Java's behavior. My question is why? Why is polymorphism generally implicit, but when it comes to generics it must be specified?
No, a List<Dog> is not a List<Animal>. Consider what you can do with a List<Animal> - you can add any animal to it... including a cat. Now, can you logically add a cat to a litter of puppies? Absolutely not.
// Illegal code - because otherwise life would be Bad
List<Dog> dogs = new ArrayList<Dog>(); // ArrayList implements List
List<Animal> animals = dogs; // Awooga awooga
animals.add(new Cat());
Dog dog = dogs.get(0); // This should be safe, right?
Suddenly you have a very confused cat.
Now, you can't add a Cat to a List<? extends Animal> because you don't know it's a List<Cat>. You can retrieve a value and know that it will be an Animal, but you can't add arbitrary animals. The reverse is true for List<? super Animal> - in that case you can add an Animal to it safely, but you don't know anything about what might be retrieved from it, because it could be a List<Object>.
What you are looking for is called covariant type parameters. This means that if one type of object can be substituted for another in a method (for instance, Animal can be replaced with Dog), the same applies to expressions using those objects (so List<Animal> could be replaced with List<Dog>). The problem is that covariance is not safe for mutable lists in general. Suppose you have a List<Dog>, and it is being used as a List<Animal>. What happens when you try to add a Cat to this List<Animal> which is really a List<Dog>? Automatically allowing type parameters to be covariant breaks the type system.
It would be useful to add syntax to allow type parameters to be specified as covariant, which avoids the ? extends Foo in method declarations, but that does add additional complexity.
The reason a List<Dog> is not a List<Animal>, is that, for example, you can insert a Cat into a List<Animal>, but not into a List<Dog>... you can use wildcards to make generics more extensible where possible; for example, reading from a List<Dog> is the similar to reading from a List<Animal> -- but not writing.
The Generics in the Java Language and the Section on Generics from the Java Tutorials have a very good, in-depth explanation as to why some things are or are not polymorphic or permitted with generics.
A point I think should be added to what other answers mention is that while
List<Dog> isn't-a List<Animal> in Java
it is also true that
A list of dogs is-a list of animals in English (under a reasonable interpretation)
The way the OP's intuition works - which is completely valid of course - is the latter sentence. However, if we apply this intuition we get a language that is not Java-esque in its type system: Suppose our language does allow adding a cat to our list of dogs. What would that mean? It would mean that the list ceases to be a list of dogs, and remains merely a list of animals. And a list of mammals, and a list of quadrapeds.
To put it another way: A List<Dog> in Java does not mean "a list of dogs" in English, it means "a list of dogs and nothing other than dogs".
More generally, OP's intuition lends itself towards a language in which operations on objects can change their type, or rather, an object's type(s) is a (dynamic) function of its value.
I would say the whole point of Generics is that it doesn't allow that. Consider the situation with arrays, which do allow that type of covariance:
Object[] objects = new String[10];
objects[0] = Boolean.FALSE;
That code compiles fine, but throws a runtime error (java.lang.ArrayStoreException: java.lang.Boolean in the second line). It is not typesafe. The point of Generics is to add the compile time type safety, otherwise you could just stick with a plain class without generics.
Now there are times where you need to be more flexible and that is what the ? super Class and ? extends Class are for. The former is when you need to insert into a type Collection (for example), and the latter is for when you need to read from it, in a type safe manner. But the only way to do both at the same time is to have a specific type.
To understand the problem it's useful to make comparison to arrays.
List<Dog> is not subclass of List<Animal>.
But Dog[] is subclass of Animal[].
Arrays are reifiable and covariant. Reifiable means their type information is fully available at runtime. Therefore arrays provide runtime type safety but not compile-time type safety.
// All compiles but throws ArrayStoreException at runtime at last line
Dog[] dogs = new Dog[10];
Animal[] animals = dogs; // compiles
animals[0] = new Cat(); // throws ArrayStoreException at runtime
It's vice versa for generics:
Generics are erased and invariant. Therefore generics can't provide runtime type safety, but they provide compile-time type safety. In the code below if generics were covariant it will be possible to make heap pollution at line 3.
List<Dog> dogs = new ArrayList<>();
List<Animal> animals = dogs; // compile-time error, otherwise heap pollution
animals.add(new Cat());
The answers given here didn't fully convince me. So instead, I make another example.
public void passOn(Consumer<Animal> consumer, Supplier<Animal> supplier) {
consumer.accept(supplier.get());
}
sounds fine, doesn't it? But you can only pass Consumers and Suppliers for Animals. If you have a Mammal consumer, but a Duck supplier, they should not fit although both are animals. In order to disallow this, additional restrictions have been added.
Instead of the above, we have to define relationships between the types we use.
E. g.,
public <A extends Animal> void passOn(Consumer<A> consumer, Supplier<? extends A> supplier) {
consumer.accept(supplier.get());
}
makes sure that we can only use a supplier which provides us the right type of object for the consumer.
OTOH, we could as well do
public <A extends Animal> void passOn(Consumer<? super A> consumer, Supplier<A> supplier) {
consumer.accept(supplier.get());
}
where we go the other way: we define the type of the Supplier and restrict that it can be put into the Consumer.
We even can do
public <A extends Animal> void passOn(Consumer<? super A> consumer, Supplier<? extends A> supplier) {
consumer.accept(supplier.get());
}
where, having the intuitive relations Life -> Animal -> Mammal -> Dog, Cat etc., we could even put a Mammal into a Life consumer, but not a String into a Life consumer.
The basis logic for such behavior is that Generics follow a mechanism of type erasure. So at run time you have no way if identifying the type of collection unlike arrays where there is no such erasure process. So coming back to your question...
So suppose there is a method as given below:
add(List<Animal>){
//You can add List<Dog or List<Cat> and this will compile as per rules of polymorphism
}
Now if java allows caller to add List of type Animal to this method then you might add wrong thing into collection and at run time too it will run due to type erasure. While in case of arrays you will get a run time exception for such scenarios...
Thus in essence this behavior is implemented so that one cannot add wrong thing into collection. Now I believe type erasure exists so as to give compatibility with legacy java without generics....
Others have done a decent job of explaining why you cannot just cast a list of descendant to list of superclass.
However, many people visit this question looking for a solution.
So, the solution to this problem since Java version 10 is as follows:
(Note: S = superclass)
List<S> supers = List.copyOf( descendants );
This function will do a cast if it is perfectly safe to do so, or a copy if a cast would not be safe.
For an in-depth explanation (which takes into consideration the potential pitfalls mentioned by other answers here) see related question and my 2022 answer to it: https://stackoverflow.com/a/72195980/773113
Subtyping is invariant for parameterized types. Even tough the class Dog is a subtype of Animal, the parameterized type List<Dog> is not a subtype of List<Animal>. In contrast, covariant subtyping is used by arrays, so the array
type Dog[] is a subtype of Animal[].
Invariant subtyping ensures that the type constraints enforced by Java are not violated. Consider the following code given by #Jon Skeet:
List<Dog> dogs = new ArrayList<Dog>(1);
List<Animal> animals = dogs;
animals.add(new Cat()); // compile-time error
Dog dog = dogs.get(0);
As stated by #Jon Skeet, this code is illegal, because otherwise it would violate the type constraints by returning a cat when a dog expected.
It is instructive to compare the above to analogous code for arrays.
Dog[] dogs = new Dog[1];
Object[] animals = dogs;
animals[0] = new Cat(); // run-time error
Dog dog = dogs[0];
The code is legal. However, throws an array store exception.
An array carries its type at run-time this way JVM can enforce
type safety of covariant subtyping.
To understand this further let's look at the bytecode generated by javap of the class below:
import java.util.ArrayList;
import java.util.List;
public class Demonstration {
public void normal() {
List normal = new ArrayList(1);
normal.add("lorem ipsum");
}
public void parameterized() {
List<String> parameterized = new ArrayList<>(1);
parameterized.add("lorem ipsum");
}
}
Using the command javap -c Demonstration, this shows the following Java bytecode:
Compiled from "Demonstration.java"
public class Demonstration {
public Demonstration();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public void normal();
Code:
0: new #2 // class java/util/ArrayList
3: dup
4: iconst_1
5: invokespecial #3 // Method java/util/ArrayList."<init>":(I)V
8: astore_1
9: aload_1
10: ldc #4 // String lorem ipsum
12: invokeinterface #5, 2 // InterfaceMethod java/util/List.add:(Ljava/lang/Object;)Z
17: pop
18: return
public void parameterized();
Code:
0: new #2 // class java/util/ArrayList
3: dup
4: iconst_1
5: invokespecial #3 // Method java/util/ArrayList."<init>":(I)V
8: astore_1
9: aload_1
10: ldc #4 // String lorem ipsum
12: invokeinterface #5, 2 // InterfaceMethod java/util/List.add:(Ljava/lang/Object;)Z
17: pop
18: return
}
Observe that the translated code of method bodies are identical. Compiler replaced each parameterized type by its erasure. This property is crucial meaning that it did not break backwards compatibility.
In conclusion, run-time safety is not possible for parameterized types, since compiler replaces each parameterized type by its erasure. This makes parameterized types are nothing more than syntactic sugar.
If you are sure that the list items are subclasses of that given super type, you can cast the list using this approach:
(List<Animal>) (List<?>) dogs
This is usefull when you want to pass the list inside of a constructor or iterate over it.
The answer as well as other answers are correct. I am going to add to those answers with a solution that I think will be helpful. I think this comes up often in programming. One thing to note is that for Collections (Lists, Sets, etc.) the main issue is adding to the Collection. That is where things break down. Even removing is OK.
In most cases, we can use Collection<? extends T> rather then Collection<T> and that should be the first choice. However, I am finding cases where it is not easy to do that. It is up for debate as to whether that is always the best thing to do. I am presenting here a class DownCastCollection that can take convert a Collection<? extends T> to a Collection<T> (we can define similar classes for List, Set, NavigableSet,..) to be used when using the standard approach is very inconvenient. Below is an example of how to use it (we could also use Collection<? extends Object> in this case, but I am keeping it simple to illustrate using DownCastCollection.
/**Could use Collection<? extends Object> and that is the better choice.
* But I am doing this to illustrate how to use DownCastCollection. **/
public static void print(Collection<Object> col){
for(Object obj : col){
System.out.println(obj);
}
}
public static void main(String[] args){
ArrayList<String> list = new ArrayList<>();
list.addAll(Arrays.asList("a","b","c"));
print(new DownCastCollection<Object>(list));
}
Now the class:
import java.util.AbstractCollection;
import java.util.Collection;
import java.util.Iterator;
import java.util.NoSuchElementException;
public class DownCastCollection<E> extends AbstractCollection<E> implements Collection<E> {
private Collection<? extends E> delegate;
public DownCastCollection(Collection<? extends E> delegate) {
super();
this.delegate = delegate;
}
#Override
public int size() {
return delegate ==null ? 0 : delegate.size();
}
#Override
public boolean isEmpty() {
return delegate==null || delegate.isEmpty();
}
#Override
public boolean contains(Object o) {
if(isEmpty()) return false;
return delegate.contains(o);
}
private class MyIterator implements Iterator<E>{
Iterator<? extends E> delegateIterator;
protected MyIterator() {
super();
this.delegateIterator = delegate == null ? null :delegate.iterator();
}
#Override
public boolean hasNext() {
return delegateIterator != null && delegateIterator.hasNext();
}
#Override
public E next() {
if(!hasNext()) throw new NoSuchElementException("The iterator is empty");
return delegateIterator.next();
}
#Override
public void remove() {
delegateIterator.remove();
}
}
#Override
public Iterator<E> iterator() {
return new MyIterator();
}
#Override
public boolean add(E e) {
throw new UnsupportedOperationException();
}
#Override
public boolean remove(Object o) {
if(delegate == null) return false;
return delegate.remove(o);
}
#Override
public boolean containsAll(Collection<?> c) {
if(delegate==null) return false;
return delegate.containsAll(c);
}
#Override
public boolean addAll(Collection<? extends E> c) {
throw new UnsupportedOperationException();
}
#Override
public boolean removeAll(Collection<?> c) {
if(delegate == null) return false;
return delegate.removeAll(c);
}
#Override
public boolean retainAll(Collection<?> c) {
if(delegate == null) return false;
return delegate.retainAll(c);
}
#Override
public void clear() {
if(delegate == null) return;
delegate.clear();
}
}
The issue has been correctly identified as related to variance but the details are not correct. A purely functional list is a covariant data functor, which means if a type Sub is a subtype of Super, then a list of Sub is definitely a subtype of a list of Super.
However mutability of a list is not the basic problem here. The problem is mutability in general. The problem is well known, and is called the Covariance Problem, it was first identified I think by Castagna, and it completely and utterly destroys object orientation as a general paradigm. It is based on previously established variance rules established by Cardelli and Reynolds.
Somewhat oversimplifying, lets consider assignment of an object B of type T to an object A of type T as a mutation. This is without loss of generality: a mutation of A can be written A = f (A) where f: T -> T. The problem, of course, is that whilst functions are covariant in their codomain, they're contravariant in their domain, but with assignments the domain and codomain are the same, so assignment is invariant!
It follows, generalising, that subtypes cannot be mutated. But with object orientation mutation is fundamental, hence object orientation is intrinsically flawed.
Here's a simple example: in a purely functional setting a symmetric matrix is clearly a matrix, it is a subtype, no problem. Now lets add to matrix the ability to set a single element at coordinates (x,y) with the rule no other element changes. Now symmetric matrix is no longer a subtype, if you change (x,y) you have also changed (y,x). The functional operation is delta: Sym -> Mat, if you change one element of a symmetric matrix you get a general non-symmetric matrix back. Therefore if you included a "change one element" method in Mat, Sym is not a subtype. In fact .. there are almost certainly NO proper subtypes.
To put all this in easier terms: if you have a general data type with a wide range of mutators which leverage its generality you can be certain any proper subtype cannot possibly support all those mutations: if it could, it would be just as general as the supertype, contrary to the specification of "proper" subtype.
The fact Java prevents subtyping mutable lists fails to address the real issue: why are you using object oriented rubbish like Java when it was discredited several decades ago??
In any case there's a reasonable discussion here:
https://en.wikipedia.org/wiki/Covariance_and_contravariance_(computer_science)
Lets take the example from JavaSE tutorial
public abstract class Shape {
public abstract void draw(Canvas c);
}
public class Circle extends Shape {
private int x, y, radius;
public void draw(Canvas c) {
...
}
}
public class Rectangle extends Shape {
private int x, y, width, height;
public void draw(Canvas c) {
...
}
}
So why a list of dogs (circles) should not be considered implicitly a list of animals (shapes) is because of this situation:
// drawAll method call
drawAll(circleList);
public void drawAll(List<Shape> shapes) {
shapes.add(new Rectangle());
}
So Java "architects" had 2 options which address this problem:
do not consider that a subtype is implicitly it's supertype, and give a compile error, like it happens now
consider the subtype to be it's supertype and restrict at compile the "add" method (so in the drawAll method, if a list of circles, subtype of shape, would be passed, the compiler should detected that and restrict you with compile error into doing that).
For obvious reasons, that chose the first way.
We should also take in consideration how the compiler threats the generic classes: in "instantiates" a different type whenever we fill the generic arguments.
Thus we have ListOfAnimal, ListOfDog, ListOfCat, etc, which are distinct classes that end up being "created" by the compiler when we specify the generic arguments. And this is a flat hierarchy (actually regarding to List is not a hierarchy at all).
Another argument why covariance doesn't make sense in case of generic classes is the fact that at base all classes are the same - are List instances. Specialising a List by filling the generic argument doesn't extend the class, it just makes it work for that particular generic argument.
The problem has been well-identified. But there's a solution; make doSomething generic:
<T extends Animal> void doSomething<List<T> animals) {
}
now you can call doSomething with either List<Dog> or List<Cat> or List<Animal>.
another solution is to build a new list
List<Dog> dogs = new ArrayList<Dog>();
List<Animal> animals = new ArrayList<Animal>(dogs);
animals.add(new Cat());
Further to the answer by Jon Skeet, which uses this example code:
// Illegal code - because otherwise life would be Bad
List<Dog> dogs = new ArrayList<Dog>(); // ArrayList implements List
List<Animal> animals = dogs; // Awooga awooga
animals.add(new Cat());
Dog dog = dogs.get(0); // This should be safe, right?
At the deepest level, the problem here is that dogs and animals share a reference. That means that one way to make this work would be to copy the entire list, which would break reference equality:
// This code is fine
List<Dog> dogs = new ArrayList<Dog>();
dogs.add(new Dog());
List<Animal> animals = new ArrayList<>(dogs); // Copy list
animals.add(new Cat());
Dog dog = dogs.get(0); // This is fine now, because it does not return the Cat
After calling List<Animal> animals = new ArrayList<>(dogs);, you cannot subsequently directly assign animals to either dogs or cats:
// These are both illegal
dogs = animals;
cats = animals;
therefore you can't put the wrong subtype of Animal into the list, because there is no wrong subtype -- any object of subtype ? extends Animal can be added to animals.
Obviously, this changes the semantics, since the lists animals and dogs are no longer shared, so adding to one list does not add to the other (which is exactly what you want, to avoid the problem that a Cat could be added to a list that is only supposed to contain Dog objects). Also, copying the entire list can be inefficient. However, this does solve the type equivalence problem, by breaking reference equality.
I see that the question has already been answered a number of times, just want to put in my inputs on the same question.
Lets us go ahead and create a simplified Animal class hierarchy.
abstract class Animal {
void eat() {
System.out.println("animal eating");
}
}
class Dog extends Animal {
void bark() { }
}
class Cat extends Animal {
void meow() { }
}
Now let us have a look at our old friend Arrays, which we know support polymorphism implicitly-
class TestAnimals {
public static void main(String[] args) {
Animal[] animals = {new Dog(), new Cat(), new Dog()};
Dog[] dogs = {new Dog(), new Dog(), new Dog()};
takeAnimals(animals);
takeAnimals(dogs);
}
public void takeAnimals(Animal[] animals) {
for(Animal a : animals) {
System.out.println(a.eat());
}
}
}
The class compiles fine and when we run the above class we get the output
animal eating
animal eating
animal eating
animal eating
animal eating
animal eating
The point to note here is that the takeAnimals() method is defined to take anything which is of type Animal, it can take an array of type Animal and it can take an array of Dog as well because Dog-is-a-Animal. So this is Polymorphism in action.
Let us now use this same approach with generics,
Now say we tweak our code a little bit and use ArrayLists instead of Arrays -
class TestAnimals {
public static void main(String[] args) {
ArrayList<Animal> animals = new ArrayList<Animal>();
animals.add(new Dog());
animals.add(new Cat());
animals.add(new Dog());
takeAnimals(animals);
}
public void takeAnimals(ArrayList<Animal> animals) {
for(Animal a : animals) {
System.out.println(a.eat());
}
}
}
The class above will compile and will produce the output -
animal eating
animal eating
animal eating
animal eating
animal eating
animal eating
So we know this works, now lets tweak this class a little bit to use Animal type polymorphically -
class TestAnimals {
public static void main(String[] args) {
ArrayList<Animal> animals = new ArrayList<Animal>();
animals.add(new Dog());
animals.add(new Cat());
animals.add(new Dog());
ArrayList<Dog> dogs = new ArrayList<Dog>();
takeAnimals(animals);
takeAnimals(dogs);
}
public void takeAnimals(ArrayList<Animal> animals) {
for(Animal a : animals) {
System.out.println(a.eat());
}
}
}
Looks like there should be no problem in compiling the above class as the takeAnimals() method is designed to take any ArrayList of type Animal and Dog-is-a-Animal so it should not be a deal breaker here.
But, unfortunately the compiler throws an error and doesn't allow us to pass a Dog ArrayList to a variable expecting Animal ArrayList.
You ask why?
Because just imagine, if JAVA were to allow the Dog ArrayList - dogs - to be put into the Animal ArrayList - animals - and then inside the takeAnimals() method somebody does something like -
animals.add(new Cat());
thinking that this should be doable because ideally it is an Animal ArrayList and you should be in a position to add any cat to it as Cat-is-also-a-Animal, but in real you passed a Dog type ArrayList to it.
So, now you must be thinking the the same should have happened with the Arrays as well. You are right in thinking so.
If somebody tries to do the same thing with Arrays then Arrays are also going to throw an error but Arrays handle this error at runtime whereas ArrayLists handle this error at compile time.

List of an interface inside a collection [duplicate]

I'm a bit confused about how Java generics handle inheritance / polymorphism.
Assume the following hierarchy -
Animal (Parent)
Dog - Cat (Children)
So suppose I have a method doSomething(List<Animal> animals). By all the rules of inheritance and polymorphism, I would assume that a List<Dog> is a List<Animal> and a List<Cat> is a List<Animal> - and so either one could be passed to this method. Not so. If I want to achieve this behavior, I have to explicitly tell the method to accept a list of any subclass of Animal by saying doSomething(List<? extends Animal> animals).
I understand that this is Java's behavior. My question is why? Why is polymorphism generally implicit, but when it comes to generics it must be specified?
No, a List<Dog> is not a List<Animal>. Consider what you can do with a List<Animal> - you can add any animal to it... including a cat. Now, can you logically add a cat to a litter of puppies? Absolutely not.
// Illegal code - because otherwise life would be Bad
List<Dog> dogs = new ArrayList<Dog>(); // ArrayList implements List
List<Animal> animals = dogs; // Awooga awooga
animals.add(new Cat());
Dog dog = dogs.get(0); // This should be safe, right?
Suddenly you have a very confused cat.
Now, you can't add a Cat to a List<? extends Animal> because you don't know it's a List<Cat>. You can retrieve a value and know that it will be an Animal, but you can't add arbitrary animals. The reverse is true for List<? super Animal> - in that case you can add an Animal to it safely, but you don't know anything about what might be retrieved from it, because it could be a List<Object>.
What you are looking for is called covariant type parameters. This means that if one type of object can be substituted for another in a method (for instance, Animal can be replaced with Dog), the same applies to expressions using those objects (so List<Animal> could be replaced with List<Dog>). The problem is that covariance is not safe for mutable lists in general. Suppose you have a List<Dog>, and it is being used as a List<Animal>. What happens when you try to add a Cat to this List<Animal> which is really a List<Dog>? Automatically allowing type parameters to be covariant breaks the type system.
It would be useful to add syntax to allow type parameters to be specified as covariant, which avoids the ? extends Foo in method declarations, but that does add additional complexity.
The reason a List<Dog> is not a List<Animal>, is that, for example, you can insert a Cat into a List<Animal>, but not into a List<Dog>... you can use wildcards to make generics more extensible where possible; for example, reading from a List<Dog> is the similar to reading from a List<Animal> -- but not writing.
The Generics in the Java Language and the Section on Generics from the Java Tutorials have a very good, in-depth explanation as to why some things are or are not polymorphic or permitted with generics.
A point I think should be added to what other answers mention is that while
List<Dog> isn't-a List<Animal> in Java
it is also true that
A list of dogs is-a list of animals in English (under a reasonable interpretation)
The way the OP's intuition works - which is completely valid of course - is the latter sentence. However, if we apply this intuition we get a language that is not Java-esque in its type system: Suppose our language does allow adding a cat to our list of dogs. What would that mean? It would mean that the list ceases to be a list of dogs, and remains merely a list of animals. And a list of mammals, and a list of quadrapeds.
To put it another way: A List<Dog> in Java does not mean "a list of dogs" in English, it means "a list of dogs and nothing other than dogs".
More generally, OP's intuition lends itself towards a language in which operations on objects can change their type, or rather, an object's type(s) is a (dynamic) function of its value.
I would say the whole point of Generics is that it doesn't allow that. Consider the situation with arrays, which do allow that type of covariance:
Object[] objects = new String[10];
objects[0] = Boolean.FALSE;
That code compiles fine, but throws a runtime error (java.lang.ArrayStoreException: java.lang.Boolean in the second line). It is not typesafe. The point of Generics is to add the compile time type safety, otherwise you could just stick with a plain class without generics.
Now there are times where you need to be more flexible and that is what the ? super Class and ? extends Class are for. The former is when you need to insert into a type Collection (for example), and the latter is for when you need to read from it, in a type safe manner. But the only way to do both at the same time is to have a specific type.
To understand the problem it's useful to make comparison to arrays.
List<Dog> is not subclass of List<Animal>.
But Dog[] is subclass of Animal[].
Arrays are reifiable and covariant. Reifiable means their type information is fully available at runtime. Therefore arrays provide runtime type safety but not compile-time type safety.
// All compiles but throws ArrayStoreException at runtime at last line
Dog[] dogs = new Dog[10];
Animal[] animals = dogs; // compiles
animals[0] = new Cat(); // throws ArrayStoreException at runtime
It's vice versa for generics:
Generics are erased and invariant. Therefore generics can't provide runtime type safety, but they provide compile-time type safety. In the code below if generics were covariant it will be possible to make heap pollution at line 3.
List<Dog> dogs = new ArrayList<>();
List<Animal> animals = dogs; // compile-time error, otherwise heap pollution
animals.add(new Cat());
The answers given here didn't fully convince me. So instead, I make another example.
public void passOn(Consumer<Animal> consumer, Supplier<Animal> supplier) {
consumer.accept(supplier.get());
}
sounds fine, doesn't it? But you can only pass Consumers and Suppliers for Animals. If you have a Mammal consumer, but a Duck supplier, they should not fit although both are animals. In order to disallow this, additional restrictions have been added.
Instead of the above, we have to define relationships between the types we use.
E. g.,
public <A extends Animal> void passOn(Consumer<A> consumer, Supplier<? extends A> supplier) {
consumer.accept(supplier.get());
}
makes sure that we can only use a supplier which provides us the right type of object for the consumer.
OTOH, we could as well do
public <A extends Animal> void passOn(Consumer<? super A> consumer, Supplier<A> supplier) {
consumer.accept(supplier.get());
}
where we go the other way: we define the type of the Supplier and restrict that it can be put into the Consumer.
We even can do
public <A extends Animal> void passOn(Consumer<? super A> consumer, Supplier<? extends A> supplier) {
consumer.accept(supplier.get());
}
where, having the intuitive relations Life -> Animal -> Mammal -> Dog, Cat etc., we could even put a Mammal into a Life consumer, but not a String into a Life consumer.
The basis logic for such behavior is that Generics follow a mechanism of type erasure. So at run time you have no way if identifying the type of collection unlike arrays where there is no such erasure process. So coming back to your question...
So suppose there is a method as given below:
add(List<Animal>){
//You can add List<Dog or List<Cat> and this will compile as per rules of polymorphism
}
Now if java allows caller to add List of type Animal to this method then you might add wrong thing into collection and at run time too it will run due to type erasure. While in case of arrays you will get a run time exception for such scenarios...
Thus in essence this behavior is implemented so that one cannot add wrong thing into collection. Now I believe type erasure exists so as to give compatibility with legacy java without generics....
Others have done a decent job of explaining why you cannot just cast a list of descendant to list of superclass.
However, many people visit this question looking for a solution.
So, the solution to this problem since Java version 10 is as follows:
(Note: S = superclass)
List<S> supers = List.copyOf( descendants );
This function will do a cast if it is perfectly safe to do so, or a copy if a cast would not be safe.
For an in-depth explanation (which takes into consideration the potential pitfalls mentioned by other answers here) see related question and my 2022 answer to it: https://stackoverflow.com/a/72195980/773113
Subtyping is invariant for parameterized types. Even tough the class Dog is a subtype of Animal, the parameterized type List<Dog> is not a subtype of List<Animal>. In contrast, covariant subtyping is used by arrays, so the array
type Dog[] is a subtype of Animal[].
Invariant subtyping ensures that the type constraints enforced by Java are not violated. Consider the following code given by #Jon Skeet:
List<Dog> dogs = new ArrayList<Dog>(1);
List<Animal> animals = dogs;
animals.add(new Cat()); // compile-time error
Dog dog = dogs.get(0);
As stated by #Jon Skeet, this code is illegal, because otherwise it would violate the type constraints by returning a cat when a dog expected.
It is instructive to compare the above to analogous code for arrays.
Dog[] dogs = new Dog[1];
Object[] animals = dogs;
animals[0] = new Cat(); // run-time error
Dog dog = dogs[0];
The code is legal. However, throws an array store exception.
An array carries its type at run-time this way JVM can enforce
type safety of covariant subtyping.
To understand this further let's look at the bytecode generated by javap of the class below:
import java.util.ArrayList;
import java.util.List;
public class Demonstration {
public void normal() {
List normal = new ArrayList(1);
normal.add("lorem ipsum");
}
public void parameterized() {
List<String> parameterized = new ArrayList<>(1);
parameterized.add("lorem ipsum");
}
}
Using the command javap -c Demonstration, this shows the following Java bytecode:
Compiled from "Demonstration.java"
public class Demonstration {
public Demonstration();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public void normal();
Code:
0: new #2 // class java/util/ArrayList
3: dup
4: iconst_1
5: invokespecial #3 // Method java/util/ArrayList."<init>":(I)V
8: astore_1
9: aload_1
10: ldc #4 // String lorem ipsum
12: invokeinterface #5, 2 // InterfaceMethod java/util/List.add:(Ljava/lang/Object;)Z
17: pop
18: return
public void parameterized();
Code:
0: new #2 // class java/util/ArrayList
3: dup
4: iconst_1
5: invokespecial #3 // Method java/util/ArrayList."<init>":(I)V
8: astore_1
9: aload_1
10: ldc #4 // String lorem ipsum
12: invokeinterface #5, 2 // InterfaceMethod java/util/List.add:(Ljava/lang/Object;)Z
17: pop
18: return
}
Observe that the translated code of method bodies are identical. Compiler replaced each parameterized type by its erasure. This property is crucial meaning that it did not break backwards compatibility.
In conclusion, run-time safety is not possible for parameterized types, since compiler replaces each parameterized type by its erasure. This makes parameterized types are nothing more than syntactic sugar.
If you are sure that the list items are subclasses of that given super type, you can cast the list using this approach:
(List<Animal>) (List<?>) dogs
This is usefull when you want to pass the list inside of a constructor or iterate over it.
The answer as well as other answers are correct. I am going to add to those answers with a solution that I think will be helpful. I think this comes up often in programming. One thing to note is that for Collections (Lists, Sets, etc.) the main issue is adding to the Collection. That is where things break down. Even removing is OK.
In most cases, we can use Collection<? extends T> rather then Collection<T> and that should be the first choice. However, I am finding cases where it is not easy to do that. It is up for debate as to whether that is always the best thing to do. I am presenting here a class DownCastCollection that can take convert a Collection<? extends T> to a Collection<T> (we can define similar classes for List, Set, NavigableSet,..) to be used when using the standard approach is very inconvenient. Below is an example of how to use it (we could also use Collection<? extends Object> in this case, but I am keeping it simple to illustrate using DownCastCollection.
/**Could use Collection<? extends Object> and that is the better choice.
* But I am doing this to illustrate how to use DownCastCollection. **/
public static void print(Collection<Object> col){
for(Object obj : col){
System.out.println(obj);
}
}
public static void main(String[] args){
ArrayList<String> list = new ArrayList<>();
list.addAll(Arrays.asList("a","b","c"));
print(new DownCastCollection<Object>(list));
}
Now the class:
import java.util.AbstractCollection;
import java.util.Collection;
import java.util.Iterator;
import java.util.NoSuchElementException;
public class DownCastCollection<E> extends AbstractCollection<E> implements Collection<E> {
private Collection<? extends E> delegate;
public DownCastCollection(Collection<? extends E> delegate) {
super();
this.delegate = delegate;
}
#Override
public int size() {
return delegate ==null ? 0 : delegate.size();
}
#Override
public boolean isEmpty() {
return delegate==null || delegate.isEmpty();
}
#Override
public boolean contains(Object o) {
if(isEmpty()) return false;
return delegate.contains(o);
}
private class MyIterator implements Iterator<E>{
Iterator<? extends E> delegateIterator;
protected MyIterator() {
super();
this.delegateIterator = delegate == null ? null :delegate.iterator();
}
#Override
public boolean hasNext() {
return delegateIterator != null && delegateIterator.hasNext();
}
#Override
public E next() {
if(!hasNext()) throw new NoSuchElementException("The iterator is empty");
return delegateIterator.next();
}
#Override
public void remove() {
delegateIterator.remove();
}
}
#Override
public Iterator<E> iterator() {
return new MyIterator();
}
#Override
public boolean add(E e) {
throw new UnsupportedOperationException();
}
#Override
public boolean remove(Object o) {
if(delegate == null) return false;
return delegate.remove(o);
}
#Override
public boolean containsAll(Collection<?> c) {
if(delegate==null) return false;
return delegate.containsAll(c);
}
#Override
public boolean addAll(Collection<? extends E> c) {
throw new UnsupportedOperationException();
}
#Override
public boolean removeAll(Collection<?> c) {
if(delegate == null) return false;
return delegate.removeAll(c);
}
#Override
public boolean retainAll(Collection<?> c) {
if(delegate == null) return false;
return delegate.retainAll(c);
}
#Override
public void clear() {
if(delegate == null) return;
delegate.clear();
}
}
The issue has been correctly identified as related to variance but the details are not correct. A purely functional list is a covariant data functor, which means if a type Sub is a subtype of Super, then a list of Sub is definitely a subtype of a list of Super.
However mutability of a list is not the basic problem here. The problem is mutability in general. The problem is well known, and is called the Covariance Problem, it was first identified I think by Castagna, and it completely and utterly destroys object orientation as a general paradigm. It is based on previously established variance rules established by Cardelli and Reynolds.
Somewhat oversimplifying, lets consider assignment of an object B of type T to an object A of type T as a mutation. This is without loss of generality: a mutation of A can be written A = f (A) where f: T -> T. The problem, of course, is that whilst functions are covariant in their codomain, they're contravariant in their domain, but with assignments the domain and codomain are the same, so assignment is invariant!
It follows, generalising, that subtypes cannot be mutated. But with object orientation mutation is fundamental, hence object orientation is intrinsically flawed.
Here's a simple example: in a purely functional setting a symmetric matrix is clearly a matrix, it is a subtype, no problem. Now lets add to matrix the ability to set a single element at coordinates (x,y) with the rule no other element changes. Now symmetric matrix is no longer a subtype, if you change (x,y) you have also changed (y,x). The functional operation is delta: Sym -> Mat, if you change one element of a symmetric matrix you get a general non-symmetric matrix back. Therefore if you included a "change one element" method in Mat, Sym is not a subtype. In fact .. there are almost certainly NO proper subtypes.
To put all this in easier terms: if you have a general data type with a wide range of mutators which leverage its generality you can be certain any proper subtype cannot possibly support all those mutations: if it could, it would be just as general as the supertype, contrary to the specification of "proper" subtype.
The fact Java prevents subtyping mutable lists fails to address the real issue: why are you using object oriented rubbish like Java when it was discredited several decades ago??
In any case there's a reasonable discussion here:
https://en.wikipedia.org/wiki/Covariance_and_contravariance_(computer_science)
Lets take the example from JavaSE tutorial
public abstract class Shape {
public abstract void draw(Canvas c);
}
public class Circle extends Shape {
private int x, y, radius;
public void draw(Canvas c) {
...
}
}
public class Rectangle extends Shape {
private int x, y, width, height;
public void draw(Canvas c) {
...
}
}
So why a list of dogs (circles) should not be considered implicitly a list of animals (shapes) is because of this situation:
// drawAll method call
drawAll(circleList);
public void drawAll(List<Shape> shapes) {
shapes.add(new Rectangle());
}
So Java "architects" had 2 options which address this problem:
do not consider that a subtype is implicitly it's supertype, and give a compile error, like it happens now
consider the subtype to be it's supertype and restrict at compile the "add" method (so in the drawAll method, if a list of circles, subtype of shape, would be passed, the compiler should detected that and restrict you with compile error into doing that).
For obvious reasons, that chose the first way.
We should also take in consideration how the compiler threats the generic classes: in "instantiates" a different type whenever we fill the generic arguments.
Thus we have ListOfAnimal, ListOfDog, ListOfCat, etc, which are distinct classes that end up being "created" by the compiler when we specify the generic arguments. And this is a flat hierarchy (actually regarding to List is not a hierarchy at all).
Another argument why covariance doesn't make sense in case of generic classes is the fact that at base all classes are the same - are List instances. Specialising a List by filling the generic argument doesn't extend the class, it just makes it work for that particular generic argument.
The problem has been well-identified. But there's a solution; make doSomething generic:
<T extends Animal> void doSomething<List<T> animals) {
}
now you can call doSomething with either List<Dog> or List<Cat> or List<Animal>.
another solution is to build a new list
List<Dog> dogs = new ArrayList<Dog>();
List<Animal> animals = new ArrayList<Animal>(dogs);
animals.add(new Cat());
Further to the answer by Jon Skeet, which uses this example code:
// Illegal code - because otherwise life would be Bad
List<Dog> dogs = new ArrayList<Dog>(); // ArrayList implements List
List<Animal> animals = dogs; // Awooga awooga
animals.add(new Cat());
Dog dog = dogs.get(0); // This should be safe, right?
At the deepest level, the problem here is that dogs and animals share a reference. That means that one way to make this work would be to copy the entire list, which would break reference equality:
// This code is fine
List<Dog> dogs = new ArrayList<Dog>();
dogs.add(new Dog());
List<Animal> animals = new ArrayList<>(dogs); // Copy list
animals.add(new Cat());
Dog dog = dogs.get(0); // This is fine now, because it does not return the Cat
After calling List<Animal> animals = new ArrayList<>(dogs);, you cannot subsequently directly assign animals to either dogs or cats:
// These are both illegal
dogs = animals;
cats = animals;
therefore you can't put the wrong subtype of Animal into the list, because there is no wrong subtype -- any object of subtype ? extends Animal can be added to animals.
Obviously, this changes the semantics, since the lists animals and dogs are no longer shared, so adding to one list does not add to the other (which is exactly what you want, to avoid the problem that a Cat could be added to a list that is only supposed to contain Dog objects). Also, copying the entire list can be inefficient. However, this does solve the type equivalence problem, by breaking reference equality.
I see that the question has already been answered a number of times, just want to put in my inputs on the same question.
Lets us go ahead and create a simplified Animal class hierarchy.
abstract class Animal {
void eat() {
System.out.println("animal eating");
}
}
class Dog extends Animal {
void bark() { }
}
class Cat extends Animal {
void meow() { }
}
Now let us have a look at our old friend Arrays, which we know support polymorphism implicitly-
class TestAnimals {
public static void main(String[] args) {
Animal[] animals = {new Dog(), new Cat(), new Dog()};
Dog[] dogs = {new Dog(), new Dog(), new Dog()};
takeAnimals(animals);
takeAnimals(dogs);
}
public void takeAnimals(Animal[] animals) {
for(Animal a : animals) {
System.out.println(a.eat());
}
}
}
The class compiles fine and when we run the above class we get the output
animal eating
animal eating
animal eating
animal eating
animal eating
animal eating
The point to note here is that the takeAnimals() method is defined to take anything which is of type Animal, it can take an array of type Animal and it can take an array of Dog as well because Dog-is-a-Animal. So this is Polymorphism in action.
Let us now use this same approach with generics,
Now say we tweak our code a little bit and use ArrayLists instead of Arrays -
class TestAnimals {
public static void main(String[] args) {
ArrayList<Animal> animals = new ArrayList<Animal>();
animals.add(new Dog());
animals.add(new Cat());
animals.add(new Dog());
takeAnimals(animals);
}
public void takeAnimals(ArrayList<Animal> animals) {
for(Animal a : animals) {
System.out.println(a.eat());
}
}
}
The class above will compile and will produce the output -
animal eating
animal eating
animal eating
animal eating
animal eating
animal eating
So we know this works, now lets tweak this class a little bit to use Animal type polymorphically -
class TestAnimals {
public static void main(String[] args) {
ArrayList<Animal> animals = new ArrayList<Animal>();
animals.add(new Dog());
animals.add(new Cat());
animals.add(new Dog());
ArrayList<Dog> dogs = new ArrayList<Dog>();
takeAnimals(animals);
takeAnimals(dogs);
}
public void takeAnimals(ArrayList<Animal> animals) {
for(Animal a : animals) {
System.out.println(a.eat());
}
}
}
Looks like there should be no problem in compiling the above class as the takeAnimals() method is designed to take any ArrayList of type Animal and Dog-is-a-Animal so it should not be a deal breaker here.
But, unfortunately the compiler throws an error and doesn't allow us to pass a Dog ArrayList to a variable expecting Animal ArrayList.
You ask why?
Because just imagine, if JAVA were to allow the Dog ArrayList - dogs - to be put into the Animal ArrayList - animals - and then inside the takeAnimals() method somebody does something like -
animals.add(new Cat());
thinking that this should be doable because ideally it is an Animal ArrayList and you should be in a position to add any cat to it as Cat-is-also-a-Animal, but in real you passed a Dog type ArrayList to it.
So, now you must be thinking the the same should have happened with the Arrays as well. You are right in thinking so.
If somebody tries to do the same thing with Arrays then Arrays are also going to throw an error but Arrays handle this error at runtime whereas ArrayLists handle this error at compile time.

Categories