By froadie


2010-04-30 14:39:39 8 Comments

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?

16 comments

@Michael Ekstrand 2010-04-30 14:44:47

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.

@Mo'in Creemers 2012-08-29 11:35:53

Or use: List<Object>

@ejaenv 2018-07-20 14:12:13

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());

@Hitesh 2012-12-04 10:43:27

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....

@Root G 2018-05-03 14:00:22

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.

@gerardw 2018-03-28 18:56:11

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>.

@Cristik 2018-03-02 07:48:43

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.

@dan b 2014-12-15 11:14:50

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();

}

}

@Radiodef 2015-05-08 21:30:36

This is a good idea, so much so that it exists in Java SE already. ; ) Collections.unmodifiableCollection

@dan b 2015-05-09 11:40:25

Right but the collection I define can be modified.

@Vlasec 2017-11-13 12:47:19

Yes, it can be modified. Collection<? extends E> already handles that behavior correctly though, unless you use it in a way that is not type-safe (e.g. casting it to something else). The only advantage I see there is, when you call the add operation, it throws an exception even if you casted it.

@outdev 2017-09-29 19:55:30

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());

@leonbloy 2017-12-15 14:41:24

It might be argued that, precisely because of that, Arrays in Java are broken,

@Cristik 2018-03-02 07:40:52

Arrays being covariant is a compiler "feature".

@einpoklum 2013-03-30 07:14:42

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 (well, 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 which can have dogs, and nothing else".

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.

@Vlasec 2017-11-13 12:14:15

Yes, human language is more fuzzy. But still, once you add a different animal to the list of dogs, it is still a list of animals, but no longer a list of dogs. The difference being, a human, with the fuzzy logic, usually has no problem realizing that.

@Yishai 2010-04-30 14:50:45

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.

@Michael Borgwardt 2010-04-30 14:55:38

Arguably, array covariance is a language design bug. Note that due to type erasure, the same behaviour is technically impossible for generic collection.

@Mark Bennett 2014-10-24 17:16:15

FYI: I mentioned your answer in stackoverflow.com/a/26551453/295802

@David Tonhofer 2017-08-15 17:52:31

"I would say the whole point of Generics is that it doesn't allow that.". You can never be sure: Java and Scala's Type Systems are Unsound: The Existential Crisis of Null Pointers (presented at OOPSLA 2016) (since corrected it seems)

@Jon Skeet 2010-04-30 14:44:06

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>.

@Ingo 2013-01-28 19:29:30

Interestingly, every list of dogs is indeed a list of animals, just like intuition tells us. The point is, that not every list of animals is a list of dogs, hence mutattion of the list by adding a cat is the problem.

@Jon Skeet 2013-01-28 19:33:11

@Ingo: No, not really: you can add a cat to a list of animals, but you can't add a cat to a list of dogs. A list of dogs is only a list of animals if you consider it in a read-only sense.

@Ingo 2013-01-28 19:41:20

@JonSkeet - Of course, but who is mandating that making a new list from a cat and a list of dogs actually changes the list of dogs? This is an arbitrary implementation decision in Java. One that goes counter to logic and intuition.

@Jon Skeet 2013-01-28 19:44:49

@Ingo: The point is that we're speaking in the context of Java, where these things are specified. I don't think that taking it out of that context is really helpful. (And even in real life, in various cases if you start adding "the wrong type of item" to a list, the list no longer makes sense.)

@Ingo 2013-01-28 19:54:27

@JonSkeet I do not want to be nitpicking, but if you included the words "subclass of" after the first occurence of "not a" in your answer, it would get very much better. Or even: "While a list of dogs is certainly a list of animals, this does unfortunately not mean that a List<Dog> is a subtype of List<Animal>"

@Jon Skeet 2013-01-28 19:58:09

@Ingo: I wouldn't have used that "certainly" to start with. If you have a list which says at the top "Hotels we might want to go to" and then someone added a swimming pool to it, would you think that valid? No - it's a list of hotels, which isn't a list of buildings. And it's not like I even said "A list of dogs is not a list of animals" - I put it in code terms, in a code font. I really don't think there's any ambiguity here. Using subclass would be incorrect anyway - it's about assignment compatibility, not subclassing.

@ruakh 2013-07-03 17:03:49

I think the problem with this line of argument is that the contract of List<Animal> actually doesn't specify that you can add any Animal to it. List implementations are allowed to be completely immutable, or to be fixed-length, or to have arbitrary restrictions on what elements you can add (e.g. based on their runtime type). Note that a Dog[] is an Animal[], with forbidden assignments being blocked at runtime. (With List<Dog> that can't be done, in general, due to erasure.) If we want to view this as something other than a failure of language design, I think we need [continued]

@ruakh 2013-07-03 17:04:37

[continued] to use examples like Comparable<Dog>, where it makes sense to say that Comparable<Dog> ("can be compared to any Dog") does not imply Comparable<Animal> ("can be compared to any Animal").

@Jon Skeet 2013-07-03 17:22:27

@ruakh: The problem is that you're then punting to execution time something which can be blocked at compile-time. And I'd argue that array covariance was a design mistake to start with.

@Dennis Kassel 2014-05-01 14:00:46

Java allows doing that example when using arrays. At compile time you wouldn't have any problems with that. But at runtime a cast exception would be thrown. Please explain me why this is possible with arrays, but not with generic lists. In my opinion this behavior is not consistent. I could imagine that the java developers decided to not keep this behavior anymore as they introduced generics.

@Jon Skeet 2014-05-01 14:02:38

@GinoBambino: Firstly, I view the covariance of arrays as a mistake to start with. (And a mistake that .NET copied, to my annoyance.) Secondly, with the way generics are implemented in Java, it couldn't be caught at execution time - a String[] knows it's really a String[] rather than an Object[], but an ArrayList<String> doesn't know that. As for why the array behaviour wasn't changed when generics were introduced - that would have been a massive breaking change. That was never going to happen.

@Dennis Kassel 2014-05-01 17:15:58

I agree with you that the covariance of arrays can quickly introduce bugs in the code, therefore it should be considered as a mistake. In contrast to Java .NET can differ between List<Object> and List<String), but the above example is also not allowed. I suppose that there are not only technical reasons that argue against this. In Java it may be (because of type erasure)^^

@augurar 2014-11-17 07:49:00

@Ingo "Who is mandating that making a new list from a cat and a list of dogs actually changes the list of dogs?" This is inherent in the nature of references. In the code snippet above, the variables named dogs and animals refer to the same object.

@atomAltera 2015-02-19 12:29:24

Is not it the same as: Cat cat = new Cat(); Object o = cat; Dog dog = (Dog) o; ? This will compile

@Jon Skeet 2015-02-19 12:41:03

@atomAltera: No, because the aim of generics is to implicitly add casts that are expected to work.

@Luca Cremonesi 2015-03-05 15:45:14

Just out of curiosity, is there a reference to the Monty Python's sketch "Confuse a cat"? Or is it just a pure coincidence?

@Jon Skeet 2015-03-05 16:02:26

@LucaCremonesi: That's just coincidence. Monty Python references elsewhere may well be intentional.

@Holger 2016-01-08 10:29:56

@Ingo: the reason why the intuition fails here is that it works with immutable lists, rather than shared references to mutable lists. Java offers such a view if you ensure immutability, i.e. you can write List<Animal> animals = Collections.unmodifiableList(dogs);. Since the animals view doesn't allow mutations, it is safe.

@Vlastimil Ovčáčík 2016-02-08 13:31:51

The whole argument here can be resolved by stating that supertyping generics parameter is safe when the generic class is immutable. @JonSkeet and @Holger clearly state it and nobody argued against it. I myself do see some interesting implications when we apply this to java.lang.Class<T>.

@Lin Ma 2017-03-20 06:46:55

Hi Jon, nice argument and vote up, but it seems array works and doesn't array has the same issue of List? Why array works, but not List? See my new post and code here => stackoverflow.com/questions/42897325/…

@Jon Skeet 2017-03-20 06:53:41

@LinMa: Arrays are designed fundamentally differently, and show problems at execution time instead of compile time.

@Yar 2017-10-17 16:41:51

When you expect a list of animals, you do not care if it is a dog, a cat, or a crocodile.

@Jon Skeet 2017-10-17 17:01:18

@Yar: You do if you're trying to add to that list of animals. If you're just consuming, that's a different matter. And that's why an IEnumerable<Dog> is an IEnumerable<Animal>, but a List<Dog> isn't a List<Animal>.

@Yar 2017-10-17 17:42:53

@Jon Skeet If I want to add a dog, a cat, a bird to a list of animals - that should be OK, because i always consume an animal, not a dog. If I cast an animal to a dog - that is my problem and responsibility to make sure it is a dog. So - still we have a misunderstanding. Maybe Java is wrong? maybe lists should be more flexible? If a developer wants to LOSE all the benefits of the list of dogs, and treat them as animals - that should be OK. List should "morph" somehow on the fly. Java should adapt to human intuition, not vice versa - in my humble opinion.

@Jon Skeet 2017-10-17 17:45:43

@Yar: Ignore my IEnumerable<T> part from the previous comment - I'd forgotten this was about Java, not C#. (They have differences, but both are similar.) But no, I don't think Java is wrong - because different pieces of code can be regarding the same object as different types. Suppose you could treat List<Dog> as a List<Animal>. Then you could have: List<Dog> dogs = new ArrayList<Dog>(); List<Animal> animals = dogs; animals.add(new Cat()); Dog dog = dogs.get(0);. What would you want that to do? I want it to fail as early as possible, at compile-time - which is what it does right now.

@Jon Skeet 2017-10-17 17:47:18

@Yar: I don't want the list to "morph" a Cat into a Dog. I want to have - and do have - ways of saying, "Hey, I'm only consuming the list, and everything should be an animal" - so you use List<? extends Animal>, and you don't get to add to the list. That seems fine to me. In general, trying to make programming languages match "human intuition" sounds great, until you realise it becomes ambiguous, and everyone has different intuition.

@Yar 2017-10-19 07:19:57

@Jon Skeet OK, thanks, now you have convinced me. With this: List<Animal> animals = dogs; animals.add(new Cat()); Dog dog = dogs.get(0);

@Saqib Javed 2017-10-30 08:24:19

how about List<? extends Animal> ?

@Jon Skeet 2017-10-30 08:25:19

@SaqibJaved: Yes, at that point you can retrieve Animal values, but not add anything - as per the last paragraph of my answer.

@Powerlord 2018-02-17 22:13:49

As a side note to the previous comment, PECS is involved here if you're writing method signatures. Producer = extends, Consumer = super. That is, if a collection is producing elements for you (i.e. you're reading from it), you use extends. However, if it's consuming elements (i.e. you're adding to it), you use super.

@user3237736 2018-04-10 20:21:45

upvote just for the entertainment value of the answer

@Alex78191 2018-04-27 09:58:38

It's contravariance.

@Jon Skeet 2018-04-27 13:38:48

@Alex78191: It's unclear to me exactly which part of this long thread you're referring to.

@Alex78191 2018-04-27 18:36:21

@JonSkeet to the answer

@Jon Skeet 2018-04-27 19:30:50

@Alex78191: You mean List<? extends Animal> animals = new ArrayList<Dog>(); ? I believe that's demonstrating covariance rather than contravariance. (List<? super Dog> dogs = new ArrayList<Animal>(); would be demonstrating contravariance.)

@Dawood ibn Kareem 2018-06-20 02:37:00

@Yar I strongly disagree. If I have a list of dogs, I want to be able to write if (! listOfDogs.isEmpty()) { listOfDogs.get(0).bark(); } - but if there's a possibility that listOfDogs has become a list of assorted dogs, cats and birds, then I'd want the compiler to stop me doing that. And if the compiler's going to do that, then I may as well just have a List<Animal> all along. Having a List<Dog> means that I can guarantee that anything I get out of the list is going to be able to do doggy things, like bark().

@Kröw 2018-07-12 21:02:48

This kind of behavior makes sense to me (whereas a List<Dog> can't be cast to List<Animal>) but what really irks me is that arrays can behave this way: ((Animal[]) new Dog[10])[0] = new Cat();. No compilation error, not even a warning. An ArrayStoreException is thrown when run of course, however.

@aurelius 2016-02-27 13:00:01

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:

  1. do not consider that a subtype is implicitly it's supertype, and give a compile error, like it happens now

  2. 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.

@sagits 2016-01-28 21:11:04

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 in a constructor or iterate over it

@Ferrybig 2016-02-01 15:33:02

This will create more problems than it actually solves

@sagits 2016-02-02 12:28:00

If you try to add a Cat to the list, sure it will create problems, but for looping purposes i think its the only non verbose answer.

@Angel Koh 2015-07-12 04:14:24

Actually you can use an interface to achieve what you want.

public interface Animal {
    String getName();
    String getVoice();
}
public class Dog implements Animal{
    @Override 
    String getName(){return "Dog";}
    @Override
    String getVoice(){return "woof!";}

}

you can then use the collections using

List <Animal> animalGroup = new ArrayList<Animal>();
animalGroup.add(new Dog());

@Michael Aaron Safyan 2010-04-30 14:46:00

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.

@glglgl 2015-02-14 21:26:06

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.

@ZhongYu 2015-07-27 20:10:48

Among the 4 versions, #2 is probably incorrect. e.g. we cannot call it with (Consumer<Runnable>, Supplier<Dog>) while Dog is subtype of Animal & Runnable

Related Questions

Sponsored Content

62 Answered Questions

[SOLVED] How do I generate random integers within a specific range in Java?

  • 2008-12-12 18:20:57
  • user42155
  • 3655207 View
  • 3087 Score
  • 62 Answer
  • Tags:   java random integer

29 Answered Questions

[SOLVED] How to create a generic array in Java?

26 Answered Questions

[SOLVED] Why not inherit from List<T>?

12 Answered Questions

[SOLVED] Why does Java have transient fields?

  • 2009-05-26 12:11:36
  • Animesh
  • 613139 View
  • 1278 Score
  • 12 Answer
  • Tags:   java field transient

14 Answered Questions

[SOLVED] Call a parent class's method from child class in Python?

2 Answered Questions

[SOLVED] java Generics polymorphism

  • 2018-07-03 08:06:58
  • sunka
  • 64 View
  • 2 Score
  • 2 Answer
  • Tags:   java

8 Answered Questions

1 Answered Questions

[SOLVED] Generics subclass behavior in java

  • 2017-04-30 08:14:25
  • Show Stopper
  • 52 View
  • 1 Score
  • 1 Answer
  • Tags:   java generics core

6 Answered Questions

4 Answered Questions

[SOLVED] Java generic parameters and inheritance

Sponsored Content