Is List≪Dog≫ a Subclass of List≪Animal≫? Why Are Java Generics Not Implicitly Polymorphic

Is ListDog a subclass of ListAnimal? Why are Java generics not implicitly polymorphic?

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

List of Dogs List of animals... What is the solution?

It depends on what you intend to do with the result. Study "PECS" it will help. In this case you can use a wildcard to solve it, but also generic methods and other techniques might be involved.

public void printNames(List<? extends Animal> list){
for(Animal a : list){
System.out.println(a.getName());
}
}

You could also just use the toString() method, which might be better.

public void printAnything(List<?> list){
for(Object a : list){
System.out.println(a.toString());
}
}

Generics subclass behavior in java

A variable of type List<? extends Animal> can be assigned a List<Animal>, a List<Cat> or a List<Dog>. Some of these lists don't allow Dogs to be added to them, while others don't allow Cats.

The compiler validates the correctness of the list.add statements based on the compile time type of the list variable, not based on the actual object assigned to it in runtime. Therefore both list.add(new Dog()); and list.add(new Cat()); are not accepted.

If you wish to be able to add both Dogs and Cats, use a variable of type List<Animal>.

If you wish to only be able to add Dogs, use a variable of type List<Dog>.

Why we can't do ListParent mylist = ArrayListchild();

Suppose we could. Then this program would have to be fine:

ArrayList<Banana> bananas = new ArrayList<Banana>();
List<Fruit> fruit = bananas;
fruit.add(new Apple());

Banana banana = bananas.get(0);

That's clearly not type safe - you've ended up with an apple in the collection of bananas.

What you can do is:

List<? extends Fruit> fruit = new ArrayList<Banana>();

this is safe, because the compiler won't then let you try to add to the list of fruit. It knows that it's a list of some kind of fruit, so you could write:

Fruit firstFruit = fruit.get(0);

but it doesn't know what exact kind of fruit it's a list of, and make sure you can't do the wrong thing.

See the Java generics FAQ another explanation.

Why is ClassT extends Base not applicable to ClassBase?

Java's generics are invariant. That means that, as a type parameter, Class<MyCustomBaseClass> means exactly that, no Class object representing a subclass of MyCustomBaseClass is allowed.

In your addClass method, you've only given an upper bound on T when defining it -- T could be a subclass of MyCustomBaseClass, e.g. your class MyCustomChildClass. The compiler disallows this call because of the mismatch.

You can widen what's allowed in _listOfClasses by providing a matching upper bound, which will allow the method addClass to compile.

private List<Class<? extends MyCustomBaseClass>> _listOfClasses;

Incidentally, because it doesn't really matter exactly what type T is in addClass, you can remove it and use a wildcard.

public void addClass(Class<? extends MyCustomBaseClass> clazz) {

Correct Generics using for List

It looks like you want

public static <T extends Animal> List<T> doSomeWork(List<T> animals){
List<T> newAnimals = new ArrayList<>();
return newAnimals;
}

Why in java generics ListInteger is a subtype of List? extends Integer?

Why in java generics List<Integer> is a subtype of List<? extends Integer>?

Because otherwise you wouldn't be able to do things such as:

List<Integer> list1 = new ArrayList<>();
List<? extends Integer> list2 = list1;

Personally, I would find the above resulting in an error to be the conter-intuitive scenario. When you have List<? extends Integer> all that's saying is that the List may contain Integer, some subtype of Integer1, or a mixture of Integer's subtypes1. A List<Integer> certainly meets those requirements.

The argument for List<? super Number> being a subtype of List<? super Integer> is very similar. If that wasn't the case then you couldn't do the following:

List<? super Number> list1 = new ArrayList<>();
List<? super Integer> list2 = list1;

All that List<? super Integer> means is the List is capable of consuming an object of type Integer or some supertype of Integer. A List<? super Number> meets those requirements.

Here are some links which may prove useful:

  • What is PECS (Producer Extends Consumer Super)?
  • Difference between <? super T> and <? extends T> in Java
  • How can I add to List<? extends Number> data structures?

I recommend browsing the linked/related sections of the above Q&As as well.


1. Saying "subtype of Integer" may be confusing since the class is final but hopefully it still makes sense.

Java Generics - Upper Bounds through layers of inheritance

Your first example doesn't compile. Both are caused by the ? extends part. The issue is that as soon as the initialization is done, the compiler already forgot the actual type. That means that after initializing livingThings, the compiler thinks it can be a List<LivingBeing>, List<Animal> or List<Cat> (all of which would allow adding cats), but also List<Dog> or List`, which don't allow cats.

If you want a list with things that can be any type of living being, you must declare it as such: List<LivingBeing>. Otherwise you can't add anything anymore except null.

The latter is true for any Collection<? extends T> for any type T. The only value that is safe for any type that matches ? extends T is null.

Why ListString is not acceptable as ListObject?

This generic question in Java may look confusing to any one who is not very familiar with Generics as in first glance it looks like String is object so List<String> can be used where List<Object> is required but this is not true. It will result in compilation error.

It does make sense if you go one step further because List<Object> can store anything including String, Integer etc but List<String> can only store Strings.

Also have a look at: Why not inherit from List<T>?



Related Topics



Leave a reply



Submit