I understand we can't add the element on the list which is the type of the extends bounds. But I am still not sure why the java compiler doesn't allow this. since the compiler can easily check that whether the adding new element is the subtype of the Generic type. Can anyone please help me on this?
-
1Maybe this answer will help you https://stackoverflow.com/questions/2745265/is-listdog-a-subclass-of-listanimal-why-arent-javas-generics-implicitly-p/2745301#2745301 – Radu Dumbrăveanu Aug 10 '17 at 15:16
-
I don't think the compiler has that information at that point, it just goes by the type of the variable. – Sean Van Gorder Aug 10 '17 at 17:28
2 Answers
This case is called Covariance, the extends
keyword allows you to read elements from a Collection
, but not to add them. This is because the compiler treats the add
operation as unsafe. For instance, you could declare any of the following lists:
List<? extends Number> myNums = new ArrayList<Integer>();
List<? extends Number> myNums = new ArrayList<Float>();
List<? extends Number> myNums = new ArrayList<Double>();
You would be able to read from them without problems, because whatever the list contains, can be upcasted to Number
. However, you are not allowed to put anything into any of them:
myNums.add(45L); //compiler error
This is due to the compiler not being able to determine the actual type of the of the generic list (could be of Integer
, of Float
or of Double
in our example). This is therefore an unsafe operation, you could be adding a Long
to an Integer
list, so the compiler doesn´t allow you.
Read this article for a deeper explanation.

- 76,500
- 11
- 62
- 80

- 3,238
- 2
- 19
- 37
-
It's not quite true that you can't invoke `add` on an upper-bounded list: the compiler let's you add literal `null`. – Andy Turner Aug 10 '17 at 10:06
-
@Andy Turner that is the case because ```null``` is acceptable for any object reference regardless if its type, so ```null``` is always safe from the compilers point of view (it might be unsafe if your code doesn't except to find nulls there but that is a separate issue). – Valentin Ruano Aug 10 '17 at 15:32
I'll use a modification of an example from here:
List<String> ls = new ArrayList<String>();
List<? extends Object> l = ls;
l.add(l, new Object()); // not legal - but assume it was!
String s = ls.get(0); // ClassCastException - ls contains
// Objects, not Strings.
You pointed very correctly that "the compiler can easily check that whether the adding new element is the subtype of the Generic type". But even so, since the compiler can not determine the type of the list l
, accepting l.add(l, new Object())
will cause a corruption of list ls
.
Thus the compiler is doing its job, it catches errors earlier than a run-time checking does.

- 1,266
- 1
- 22
- 32