I'm using Java 8. During training to passing Java OCP 8 I find some snippets of code that I don't understand and want to know, why it so strange for me.
I have next hierarchy:
class A {}
class B extends A {}
class C extends B {}
The first one, this code is work:
List<?> list1 = new ArrayList<A>() {
{
add(new A());
}
};
But next code doesn't work, compilation error:
list1.add(new A());
So, why we can't add new record in this way?
The second one, this code is work:
List<? extends A> list2 = new ArrayList<A>() {
{
add(new A());
add(new B());
add(new C());
}
};
But next code doesn't work, compilation error:
list2.add(new A());
list2.add(new B());
list2.add(new C());
And the last one, this code is work:
List<? super B> list3 = new ArrayList<A>() {
{
add(new A());
add(new B());
add(new C());
}
};
But in the next code, when we adding new A(), compilation error:
list3.add(new A()); // compilation error
list3.add(new B());
list3.add(new C());
Thanks for your answers!
This is a compilation error designed to enforce type safety. If the compiler allowed you to do it, imagine what could happen:
For issue 1, once the object list1
has been declared, the compiler only considers the declared type, which is List<?>
and ignores the fact that it was most recently assigned to an ArrayList<A>
.
List<?> list1 = ...; // The compiler knows list1 is a list of a certain type
// but it's not specified what the type is. It could be
// a List<String> or List<Integer> or List<Anything>
list1.add(new A()); // What if list1 was e.g. a List<String>?
But:
List<?> list1 = new ArrayList<A>() {
{
add(new A());
}
};
Here, you are assigning to list1
an expression. The expression itself, i.e. everything after =
, doesn't use ?
, and is in fact an anonymous class that extends ArrayList<A>
and has an initializer block that calls add(new A())
which is ok.
The second issue (with list2) has the same cause.
In the third issue,
List<? super B> list3 = new ArrayList<A>() {
{
add(new A());
add(new B());
add(new C());
}
};
list3.add(new A()); // compilation error
The compiler sees list3
as a List<? super B>
. This means the generic parameter can be B
or its superclass, A
. What if it's a List<B>
? You can't add an A
to a List<B>
; therefore the compiler rejects this code.