Generics and inheritance?
public static void main(String... args) {
List<Child> list1 = new ArrayList<Child>();
method2(list1);
}
public static void method2(List<Parent> list1) {
}
I am getting below compilation error
Method method2 (List) undefined ...
The above problem can be resolved by changing List<Parent> list1
to List<? extends Parent> list1
.
But if i try to add child object like below
public static void method2(List<? extends Parent> list1) {
Child child1 = new Child();
list1.add(child1);
}
It gives compilation error again
The add (capture # 1-of? Extends Parent) method on the List type is not applicable for the (Child) arguments
So my question is, if List<Child>
it is possible to pass as a parameter to List<? extends Parent> list1
, why can't we add a child to List<? extends Parent>
?
source to share
This is a very common misunderstanding. The fact that Child extends Parent does not extend List<Child>
List<Parent>
. It sounds very unintuitive, as in this case, but there is. From the java tutorial:
Given two specific types A and B (for example, number and integer), MyClass <A> is irrelevant to MyClass <B>, regardless of A and B. The common parent of MyClass and MyClass is an object.
Read this for more details .
As for adding to a list, the short answer is: Imagine you have another one class Child2 extends Parent
, now the list you receive as a parameter in method2(List<? extends Parent> list1)
can be List<Child1>
either List<Child2>
, So given that the second case is possible, adding a Child1 object is not safe type.
Now, the fact that you cannot add does not mean that you cannot do other useful things like getting the size, etc.
source to share
So my question is, if
List<Child>
it is possible to pass as a parameter toList<? extends Parent>
list1
, why can't we add a child toList<? extends Parent>
?
Suppose we could. Now suppose we had:
class Parent {}
class Mother extends Parent {}
class Father extends Parent {}
static void m(List<? extends Parent> parents) {
parents.add(new Father());
}
List<Mother> mothers = new ArrayList<>();
m(mothers);
// throws 'ClassCastException: cannot cast Father to Mother'
Mother actuallyAFather = mothers.get(0);
A List<? extends Parent>
is a list that stores at most, Parent
or perhaps some subtype Parent
that we no longer know about. We cannot add anything to it except null.
So, perhaps you want:
public static void method2(List<? super Child> list1) {
// ^^^^^^^^^^^
Child child1 = new Child();
list1.add(child1);
}
A List<? super Child>
is a list that we can add a Child
to. Maybe this, List<Parent>
or maybe this List<Child>
, but we don't care. We don't care if it's a List, we can add a Child
to.
Also see:
source to share
From Wildcards
List<? extends Parent>
is an example of a restricted template.? denotes an unknown type, just like the wildcards we saw earlier. However, in this case, we know that this unknown type is in fact a subtypeParent
.
So make sure your class Child
extends the class Parent
. then below code will work.
public static void method2(List<? extends Parent> list1) {
With this change, it should work by seeing the Output of your program
source to share
Here is my own answer after reading more material
We cannot assign List<Dog> to List<Animal>
because it List<Animal>
means that we can add any animal to it like cat, dog whereas List<Dog>
means we can only add dogs / subtypes to it, but not cats. So the assignor will think it will only get the dog / subtypes but it can also get the cat object (so runtime error)
Here's an example
psvm{
List<Dog> list1 = new ArrayList<Dog>();
list1.add(new Dog());
method1(list1);// compilation error
}
public static void method1(List<Animal> list1) {
list1.add(cat); // Good
list1.add(dog); // Good
}
Another common misconception is upper bounded generalizations which are exactly the opposite
When we say List<? extends Animal> list1
, we cannot add any value other than null, but yes, we can assign any Animal / Sub types.If we allow addition, that means it can contain any animal, such as a cat, a dog, but when retrieving ( i.e. dropping) we don't make it actually a type.
psvm{
List<Dog> list1 = new ArrayList<Dog>();
list1.add(new Dog());
method1(list1);// good
}
public static void method1(List<? extends Animal> list1) {
list1.add(cat); // compilation error
list1.add(dog); // compilation error
}
source to share