22

I have a list with upper bound generics.

 List<? extends Number> l = new ArrayList<>();
 l.add(new Integer(3));  //ERROR
 l.add(new Double(3.3)); // ERROR

I don't understand the problem, because Integer and Double extend Number.

Nikolas Charalambidis
  • 40,893
  • 16
  • 117
  • 183
user2693979
  • 2,482
  • 4
  • 24
  • 23

7 Answers7

43

List<? extends Number> does not mean "a list that can hold all objects of subclasses of Number", it means "a list parameterized to one concrete class that extends Number". It's not the contents of the list itself you are defining, it's what the parameterized type of the actual list-object assigned to the variable can be (boy, this is harder to explain than it is to understand :) )

So, you can do:

List<? extends Number> l = new ArrayList<Integer>();
List<? extends Number> l = new ArrayList<Double>();

If you want a list that is able to hold any object of class Number or its subclasses, just do this:

List<Number> l = new ArrayList<>();

l.add(new Integer(33));
l.add(new Double(33.3d));

(The boxing of the values inserted is unnecessary, but there for clarity..)

Tobb
  • 11,850
  • 6
  • 52
  • 77
9

Upper bounded and unbounded wildcard collections are immutable.

For example, you cannot do:

List<? extends Number> myList = new ArrayList<Integer>();
myList.add(new Integer(3)); //will not compile

This fails to compile because java does not know what type of List List<? extends Number> is at compilation time.

So the example above, at compile time, myList could be List<Double>or List<Integer> or a List of any subclass of Number. And since you cannot add a Double to a List<Integer> or vise-versa, the compilation fails.

Test1 Test2
  • 327
  • 2
  • 9
  • Ok so could you give me an example of a list instantiation using "upper bound" and adding elements to it. I don't think it's possible! When creating a list you can only use "lower bound" since it's never going to be immutable. "Upper bound" can still be used with a Collection in a method parameter. Could you confirm this. – CBA110 Feb 15 '18 at 21:23
  • 2
    "Upper bounded and unbounded wildcard collections are immutable" - not exactly, they can still have elements removed and can also be cleared :-) – Zippy Apr 05 '18 at 18:50
  • element of which type ?? – aName Jul 04 '18 at 20:50
  • Your explanation is simple and understandable. – Dinh Quang Tuan Jun 19 '22 at 09:23
6

The problem with upper-bounded generics is that the compiler doesn't know the exact type that is going to be used, for example:

List<? extends Number> upperBounded = new ArrayList<Integer>();
upperBounded = new ArrayList<Double>();

upperBounded can be a List of Integers as well as a list of Doubles or any other descendant of Number. Now imagine what will happen if Java allowed us to add any subclass of Number to that List.

List<? extends Number> upperBounded = new ArrayList<Integer>();
upperBounded.add(1.0); // compile-time error

Good thing that Java prevents us from doing that because this would introduce a lot of bugs.

The same applies to the unbounded generic types.

Now what about the lower-bounded generics?

List<? super Integer> lowerBounded = new ArrayList<Integer>();
lowerBounded.add(0);

This is is perfectly fine because we are safe to assume that we can add Integers to any List of an Integer superclass and this will not lead to inconsistency, for example:

List<? super Integer> lowerBounded = new ArrayList<Number>();
lowerBounded.add(0);

Here we have a reference that allows a List of Integers or one of the Integer supertypes. ArrayList of Numbers fits into that definition, so we are able to assign it to that reference. Then we can add an Integer to this list, and this is also fine because a List of Numbers can contain an Integer (because Integer is a Number).

It may be surprising, but you can't add anything not assignable to Integer to this list for the same reason as I explained above for lower-bounded generics.

List<? super Integer> lowerBounded = new ArrayList<Number>();
lowerBounded.add(1.0); // compile-time error

As the compiler does not know the List of which exact type will be used, it does not allow us to add anything that could potentially break the promise given by generics.

Hope that helps.

flounder
  • 71
  • 1
  • 3
  • why can't the compiler just add the value as Number to the list for the upperbounded wildcard, it should be fine? just like we can read from upper bounded list item as Number – HKIT Oct 09 '22 at 03:48
  • @HKIT then we would still have this problem:
    List integers = new ArrayList
    List extends Number> numbers = integers;
    // now 'numbers' and 'integers' refer to the same list
    numbers.add(1.0); // 1.0 extends Number but not Integer, adding this to 'integers' would be an error
    for (Integer integer : integers) {
        // and we would have problems with casting
        System.out.println(integer);
    }
    
    – flounder Oct 19 '22 at 11:03
2

Because List<? extends Number> means that your variable l holds a value of type List with concrete (but unknown!) type argument that extends Number.

You can add only null, because l can hold a List<MyClass> for example, where MyClass is your class that extends Number, but nor Integer, nor Double value can be casted to MyClass.

Filipp Voronov
  • 4,077
  • 5
  • 25
  • 32
  • why can't we add Number type, all elements inside are Number, just like we can get list item as number. – HKIT Oct 09 '22 at 03:54
  • because for example in a cell with type `Lost extends Number>` can be `List` and you can't add `Number` in case it's 3.14 – Filipp Voronov Oct 17 '22 at 07:25
1

I will add one more way to add the subtypes of Number to this list. i.e

List<? super Number> l = new ArrayList<>();
 l.add(new Integer(3));  //OK
 l.add(new Double(3.3)); //OK

This is allowed since the list is parameterized to be any unknown supertype of Number class. so, compiler allows the known subtype of Number. i.e Integer and Double types

Keerthivasan
  • 12,760
  • 2
  • 32
  • 53
0

Yes in case of

List<? extends Number> 

this is just a reference, may be the actual object will be

List<Integer>

so you should not be allowed to add new Double(5.0) in a list of Integer.

-1

So why cant I add element in upper bound generics ?.

 List<? extends Number> L = new ArrayList<>();
 L.add(new Integer(3));  //ERROR
 L.add(new Double(3.3)); // ERROR

I believe 'L' is a reference variable that can POINTS to ArrayList of Numbers or sub-class of Number (Integer , double ,etc...) .

In some point in time , if the 'L' referred to the ArrayList of Integer, this mean it is weird to add a Double value to the list, Why because 'L' is just a reference variable pointed to the ArrayList of Integers and u cant add a anything other than Integer..

So can I add Integer to it if 'L' is pointed to List of Integer. Well u cant , because here 'L' is Upper bounded of Number and complier is not sure whether it points to the List of integer , numbers , double or some other sub class of Number, so it says, I am not going add anything to the list even though L is pointing to arraylist of integers and u are trying to add an integer like 3 .

It is good not to add an integer in above situation because in some point in time the 'L' can be point to list of double and poor me trying to add something other than double...(imagine u r placing List<? extends Number> L as method parameter or something and some one passes list of Integer one time , float the next day) . Please correct me if i am wrong ..seri right varen !