user2693979
user2693979

Reputation: 2542

Why can't add element in a upper bound generics List?

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.

Upvotes: 22

Views: 5725

Answers (6)

flounder
flounder

Reputation: 101

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.

Upvotes: 6

Tobb
Tobb

Reputation: 12205

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..)

Upvotes: 43

Test1 Test2
Test1 Test2

Reputation: 327

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.

Upvotes: 9

punitusingh
punitusingh

Reputation: 35

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.

Upvotes: 0

Keerthivasan
Keerthivasan

Reputation: 12880

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

Upvotes: 1

Filipp Voronov
Filipp Voronov

Reputation: 4197

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.

Upvotes: 2

Related Questions