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