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 Dog
s to be added to them, while others don't allow Cat
s.
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 Dog
s and Cat
s, use a variable of type List<Animal>
.
If you wish to only be able to add Dog
s, 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 ofList<? 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 Integer
1, 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
Remove HTML Tags from a String
Android - Get Real Path of a .Txt File Selected from the File Explorer
Concurrent Modification Exception: Adding to an Arraylist
Run a Java Application as a Service on Linux
How to Compare Strings in Java
Int Division: Why Is the Result of 1/3 == 0
Change Date Format in a Java String
Difference Between "Text" and New String("Text")
Java: Splitting a Comma-Separated String But Ignoring Commas in Quotes
How to Send Http Request in Java
How to Discover Memory Usage of My Application in Android
Custom Listview Adapter Getview Method Being Called Multiple Times, and in No Coherent Order
Passing Data Through Intent Using Serializable
"No X11 Display Variable" - What Does It Mean
Is Java "Pass-By-Reference" or "Pass-By-Value"
How Do Servlets Work? Instantiation, Sessions, Shared Variables and Multithreading