1
List<Integer> ints = new ArrayList<Integer>();
 ints.add(1); ints.add(2);
 List<? extends Number> nums = ints;
 nums.add(3.14);   // compile-time error
assertints.toString().equals("[1, 2, 3.14]");

Why we are getting compile time error?

3 Answers3

1

This is because of type erasure in java generics. You can't add new elements to listbecause its type parameter is undefined at compile time. The list List<? extends Number> nums means that you can't call add method on it.

Jay Smith
  • 2,331
  • 3
  • 16
  • 27
1

List<? extends Number> means that we don't know what type is in the list, other than the fact that it is a Number.

In this case, it is a List<Integer>. Even when you assign it to a List<? extends Number>, it fundamentally remains a List<Integer>. And you can't add 3.14 to such a list.

If this were allowed, the following code would be valid:

List<Integer> ints = new ArrayList<Integer>();
ints.add(1); ints.add(2);
List<? extends Number> nums = ints;
nums.add(3.14);
Integer third = ints.get(2);

But ints.get(2) is not an integer, so it would throw an exception here. Better to catch these kinds of issues at compile time.

Joe C
  • 15,324
  • 8
  • 38
  • 50
  • I think saying `nums` somehow remains a list of Integers is a bit inaccurate. Not only you can't add floats, you can't even add integers to `nums` list. `nums.add(new Integer(1))` still will not compile. – jrook Apr 11 '17 at 06:15
  • Agree with @jrook... at the point where the error is occurring, the compiler only knows that `nums` is a `List extends Number>`. It doesn't keep track of what might have been assigned into `nums`. Therefore, you can't get a compile-time error based on `nums` being a list of `Integer`. That kind of error could be caught at run-time, although in this particular case it might not be catchable because of type erasure. – ajb Apr 11 '17 at 06:21
0

I think the important part here is the instantiation (new ArrayList<Integer>()), which clearly passes Integer to the List class. The left-hand side diamond notation doesn't really matter that much as long as it's compatible. Since you later on defined the generic as an upper boundary of Integer, mainly Number and its subtypes, Java is okay with that, because it still can deal with the Integers. The object essentially still is a List<Integer>.

This is like assigning a String to an Object. It will work because Object is a supertype of String. Here the List<? extends Number> is kind of like a supertype of List<Integer>. (I know the word "supertype" is incorrect here, so feel free to correct me.)

The instantiation with generics is sort of like an instantiation of the class itself and an object. Some other languages, e.g. Python are using the term "metaclasses" for this sort of behavior.

Hubert Grzeskowiak
  • 15,137
  • 5
  • 57
  • 74
  • I've seen "superclass" used, so if "supertype" is wrong, it's not that far wrong. – ajb Apr 11 '17 at 06:23