I have a List which is declared like this :
List<? extends Number> foo3 = new ArrayList<Integer>();
I tried to add 3 to foo3. However I get an error message like this:
The method add(capture#1-of ? extends Number) in the type List<capture#1-of ?
extends Number> is not applicable for the arguments (ExtendsNumber)
Sorry, but you can't.
The wildcard declaration of List<? extends Number> foo3 means that the variable foo3 can hold any value from a family of types (rather than any value of a specific type). It means that any of these are legal assignments:
List<? extends Number> foo3 = new ArrayList<Number>(); // Number "extends" Number
List<? extends Number> foo3 = new ArrayList<Integer>(); // Integer extends Number
List<? extends Number> foo3 = new ArrayList<Double>(); // Double extends Number
So, given this, what type of object could you add to List foo3 that would be legal after any of the above possible ArrayList assignments:
You can't add an Integer because foo3 could be pointing at a List<Double>.
You can't add a Double because foo3 could be pointing at a List<Integer>.
You can't add a Number because foo3 could be pointing at a List<Integer>.
You can't add any object to List<? extends T> because you can't guarantee what kind of List it is really pointing to, so you can't guarantee that the object is allowed in that List. The only "guarantee" is that you can only read from it and you'll get a T or subclass of T.
The reverse logic applies to super, e.g. List<? super T>. These are legal:
List<? super Number> foo3 = new ArrayList<Number>(); // Number is a "super" of Number
List<? super Number> foo3 = new ArrayList<Object>(); // Object is a "super" of Number
You can't read the specific type T (e.g. Number) from List<? super T> because you can't guarantee what kind of List it is really pointing to. The only "guarantee" you have is you are able to add a value of type T (or any subclass of T) without violating the integrity of the list being pointed to.
The perfect example of this is the signature for Collections.copy():
public static <T> void copy(List<? super T> dest, List<? extends T> src)
Notice how the src list declaration uses extends to allow me to pass any List from a family of related List types and still guarantee it will produce values of type T or subclasses of T. But you cannot add to the src list.
The dest list declaration uses super to allow me to pass any List from a family of related List types and still guarantee I can write a value of a specific type T to that list. But it cannot be guaranteed to read the values of specific type T if I read from the list.
So now, thanks to generics wildcards, I can do any of these calls with that single method:
// copy(dest, src)
Collections.copy(new ArrayList<Number>(), new ArrayList<Number>());
Collections.copy(new ArrayList<Number>(), new ArrayList<Integer>());
Collections.copy(new ArrayList<Object>(), new ArrayList<Number>());
Collections.copy(new ArrayList<Object>(), new ArrayList<Double>());
Consider this confusing and very wide code to exercise your brain. The commented out lines are illegal and the reason why is stated to the extreme right of the line (need to scroll to see some of them):
List<Number> listNumber_ListNumber = new ArrayList<Number>();
//List<Number> listNumber_ListInteger = new ArrayList<Integer>(); // error - can assign only exactly <Number>
//List<Number> listNumber_ListDouble = new ArrayList<Double>(); // error - can assign only exactly <Number>
List<? extends Number> listExtendsNumber_ListNumber = new ArrayList<Number>();
List<? extends Number> listExtendsNumber_ListInteger = new ArrayList<Integer>();
List<? extends Number> listExtendsNumber_ListDouble = new ArrayList<Double>();
List<? super Number> listSuperNumber_ListNumber = new ArrayList<Number>();
//List<? super Number> listSuperNumber_ListInteger = new ArrayList<Integer>(); // error - Integer is not superclass of Number
//List<? super Number> listSuperNumber_ListDouble = new ArrayList<Double>(); // error - Double is not superclass of Number
//List<Integer> listInteger_ListNumber = new ArrayList<Number>(); // error - can assign only exactly <Integer>
List<Integer> listInteger_ListInteger = new ArrayList<Integer>();
//List<Integer> listInteger_ListDouble = new ArrayList<Double>(); // error - can assign only exactly <Integer>
//List<? extends Integer> listExtendsInteger_ListNumber = new ArrayList<Number>(); // error - Number is not a subclass of Integer
List<? extends Integer> listExtendsInteger_ListInteger = new ArrayList<Integer>();
//List<? extends Integer> listExtendsInteger_ListDouble = new ArrayList<Double>(); // error - Double is not a subclass of Integer
List<? super Integer> listSuperInteger_ListNumber = new ArrayList<Number>();
List<? super Integer> listSuperInteger_ListInteger = new ArrayList<Integer>();
//List<? super Integer> listSuperInteger_ListDouble = new ArrayList<Double>(); // error - Double is not a superclass of Integer
listNumber_ListNumber.add(3); // ok - allowed to add Integer to exactly List<Number>
// These next 3 are compile errors for the same reason:
// You don't know what kind of List<T> is really
// being referenced - it may not be able to hold an Integer.
// You can't add anything (not Object, Number, Integer,
// nor Double) to List<? extends Number>
//listExtendsNumber_ListNumber.add(3); // error - can't add Integer to *possible* List<Double>, even though it is really List<Number>
//listExtendsNumber_ListInteger.add(3); // error - can't add Integer to *possible* List<Double>, even though it is really List<Integer>
//listExtendsNumber_ListDouble.add(3); // error - can't add Integer to *possible* List<Double>, especially since it is really List<Double>
listSuperNumber_ListNumber.add(3); // ok - allowed to add Integer to List<Number> or List<Object>
listInteger_ListInteger.add(3); // ok - allowed to add Integer to exactly List<Integer> (duh)
// This fails for same reason above - you can't
// guarantee what kind of List the var is really
// pointing to
//listExtendsInteger_ListInteger.add(3); // error - can't add Integer to *possible* List<X> that is only allowed to hold X's
listSuperInteger_ListNumber.add(3); // ok - allowed to add Integer to List<Integer>, List<Number>, or List<Object>
listSuperInteger_ListInteger.add(3); // ok - allowed to add Integer to List<Integer>, List<Number>, or List<Object>
You can't (without unsafe casts). You can only read from them.
The problem is that you don't know what exactly the list is a list of. It could be a list of any subclass of Number, so when you try to put an element into it, you don't know that the element actually fits into the list.
For example the List might be a list of Bytes, so it would be an error to put a Float into it.
It has been confusing to me even though I read answers here, until I found the comment by Pavel Minaev:
Note that List < ? extends Number > does not mean "list of objects of
different types, all of which extend Number". It means "list of
objects of a single type which extends Number"
After this I was able to understand BertF awesome explanation. List < ? extends Number > means ? could be of any type extending Number(Integer, Double, etc) and its not clearified in declaration ( List < ? extends Number > list ) that which of them it is, so when u wanna use add method its not known if the input is of the same type or not; what is the type at all?
So the elements of List < ? extends Number > could only be set when constructing.
Also note this: When we're using templates we are telling the compiler what type we're messing with. T for example holds that type for us, but not ? does the same
I gotta say.. This is one of the dirty ones to explain/learn
"List '<' ? extends Number> is actually an upper bound wildcard !
The upper-bounded wildcard says that any class that extends Number or Number itself can be used as the formal parameter type:
The problem stems from the fact that Java doesn’t know what type List really is.
It has to be an EXACT and UNIQUE Type. I hope it helps :)
You could do this instead:
List<Number> foo3 = new ArrayList<Number>();
foo3.add(3);
You can fudge it by creating a reference to the List with a different type.
(These are the "unsafe casts" mentioned by sepp2k.)
List<? extends Number> list = new ArrayList<Integer>();
// This will not compile
//list.add(100);
// WORKS, BUT NOT IDEAL
List untypedList = (List)list;
// It will let you add a number
untypedList.add(200);
// But it will also let you add a String! BAD!
untypedList.add("foo");
// YOU PROBABLY WANT THIS
// This is safer, because it will (partially) check the type of anything you add
List<Number> superclassedList = (List<Number>)(List<?>)list;
// It will let you add an integer
superclassedList.add(200);
// It won't let you add a String
//superclassedList.add("foo");
// But it will let you add a Float, which isn't really correct
superclassedList.add(3.141);
// ********************
// So you are responsible for ensuring you only add/set Integers when you have
// been given an ArrayList<Integer>
// ********************
// EVEN BETTER
// If you can, if you know the type, then use List<Integer> instead of List<Number>
List<Integer> trulyclassedList = (List<Integer>)(List<?>)list;
// That will prevent you from adding a Float
//trulyclassedList.add(3.141);
System.out.println("list: " + list);
Because untypedList, superclassedList and trulyclassedList are just references to list, you will still be adding elements to the original ArrayList.
You don't actually need to use (List<?>) in the example above, but you might need it in your code, depending on the type of list you were given.
Note that using ? will give you compiler warnings, until you put this above your function:
#SuppressWarnings("unchecked")
Because 3 is primitive (int), which does not extend Number, you can add its boxed type (Integer.valueOf(3)). Although it is displayed as an error in IDEA, it can still execute normally.
Related
Why the below code is showing a compilation error. Doesn't extend mean it can take any subclass of Number, Also compilation is shown in the add method not while defining the List if it doesn't support this construct it should show compilation error while declaring.
List<? extends Number> list = new ArrayList<Integer>();
list.add(1)
Number literals are of type int. Using construction <? extends Number> you ensure, that you can pass only instances of Number class, or any classes, that Number inherits from (that is java.lang.Object). If you want to add subtypes of Number class, e.g. Integers you should use <? super Number> (it means: for whose Number is a super class)
You may want to use:
List<? super Number> list = new ArrayList<>();
list.add(1); //no compile-time error
However, if you want to use elements of the collection, you may use them as instances of classes that extends Number class.
List<? super Number> list = new ArrayList<>();
list.add(1);
list.add(2);
print(list);
List<Object> objs = new ArrayList<>();
objs.add(1);
print(objs);
List<Number> nums = new ArrayList<>();
nums.add(1);
print(nums);
}
public static void print(List<? super Number> list){ // you can pass List of instances of type Number or upper
for(Object num: list){ // but you have to refer to them as Objects (it is an upper class of Number
System.out.println(num);
}
}
If I have this,
Collection<? extends Number> c = new ArrayList<>();
c.add(new Integer(1)); // Compile time error
Since we don't know what the element type of c stands for, we cannot add integers to it.
But if I do like,
List<List<? extends Number>> history = new ArrayList<>();
List<Integer> integers = new ArrayList<>();
integers.add(new Integer(1));
List<Double> doubles = new ArrayList<>();
doubles.add(new Double(2));
history.add(integers); // This is allowed
history.add(doubles); // This is allowed
Why the addition in the 2nd example is allowed?
Collection<? extends ...>
c.add(...);
A collection with a lower bound cannot be added to.
List<List<...>> history;
history.add(...); // Allowed
The outer list has a concrete type. The ? extends wildcard is inside the inner list, but it's irrelevant since you're adding to the outer list. I've replaced the wildcard with ... since it doesn't matter what it is when you're calling history.add().
If the outer list had a wildcard bound then adding would fail.
List<? extends List<...>> history;
history.add(...); // NOT allowed
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.
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.
Given the types List<?>, List<Object>, List<? super Number>, List<Number>, List<Integer>, and List<? extends Number>, I am trying to understand their hierarchy.
I know that List<Integer> is NOT a subtype of List<Number>, even though Integer is indeed a subtype of Number, so I thought that it was instead a subtype of List<? extends Number>.
But List<? extends Number> intuitively seems to be a subtype of List<Number>, which makes List<Integer> a descendant of List<Number> after all, as my idea of it shows:
So, if a type is descended from another, but not directly, is it still a subtype of its ancestor (or I am just wrong in the diagram)? This exercise has also gotten me a bit confused about ? vs. Object...actually, it looks like I might have List<Object> and List<? super Number> mixed up. I guess one of the biggest questions is, "Is 'everything' an Object...or is everything a ? ?" Or both...or neither?
Reason why List<Integer> is not a sub type of List<Number>.
lets just consider a scenario where a List<Number> accepts List<Integer>(which would practically wouldn't work).
List<Integer> listI =new ArrayList<Integer>();
List<Double> listD = new ArrayList<Double>();
method(listI);
method(listD);
and you have a method which takes List<Number> as an argument.
public void method(List<Number> list) {
list.add(new Double()); // would fail if you pass an List<Integer> as Integer is not a super type of Integer.
list.add(new Integer()); //would fail if you pass an List<Double> as Double is not a subtype of Integer
}
so if your method argument declared as List<number> accepts List<Double> and you try to add an Integer into the list of Double.which is wrong, because Double is not a super type of Integer thus List<Number> is not a sub type List<Integer>.
now consider the same scenario where List<? extends Number> is a method argument instead of List<Number>
public void method(List<? extends Number> list) {
// list.add(new Double());
// list.add(new Integer());
}
and now you pass an List<Integer> to this method. if it accepts it and you try to add a Double to list of Integers. BOOM... you just added a Double to List of Integer .
To give an informal example . consider an Animal supertype with Cat and Dog as subtypes.
now, you have a method which accepts <? extends Animal> and you pass an Dog to this method. and in the method you try to add an Cat. its adding a wrong type (a CAT) to your Dog, thus **List<Dog>** is not a subtype of List<? extends Animal>
please let me know if this is not expressive enough.Thanks :)
I have a function whose arguments are:
myFn(String, List<Number>, List<Number>)
The calling function has two objects of List which I want to use as the 2nd and third argument of the function. I get the compilation error:
The method myFn(String, List<Number>, List<Number>) in the type MyLib is not applicable for the arguments (String, List<Double>, List<Double>)
Is there an easy way to overcome this? Is there an easy way to cast the list of Double into a List of Number?
You cannot cast List<Double> to a List<Number> because the following code would break your List<Double>
List<Double> list = new List<Double>();
List<Number> list1 = list; // This does not work
list1.add(new Integer(0));
Double d = list.get(0);// Hu-hoh you received an Integer, not a Double!!
List<? extends Number> list2 = list; // This does work
What you could do, is change
myFn(String, List<Number>, List<Number>)
to
myFn(String, List<? extends Number>, List<? extends Number>)
change myFn definition to <T extends Number> myFn(String, List<T>, List<T>, Class<T> type)
and you can call it as myFn(String, List<Double>, List<Double>, Double.class)
If you know that the list won't get modified by the method, just use
List<Number> unmodifiableView = Collections.unmodifiableList(doubleList);
which runs in constant time and returns a view. It's perfectly type-safe, efficient, and legal -- so long as you know the list won't get modified.
You can use
myFn(String, List<? extends Number>, List<? extends Number>)
or call your function with
myFn(String, (List) list1, (List) list2); // produces a warning.
You cannot cast Collections like this. This Java tutorial gives you the full explanation.
As a solution, where you are calling the method just create a new List with the Double List as a parameter:
List<Double> doubleList = new ArrayList<Double>();
myFn(new ArrayList<Number>(doubleList));
If you can't change the myFn method, then cheating is an option:
private <returntype> myFnAdapter(String s , List<Double> l1Double, List<Double> l2Double) {
// the following lines do create warnings
// it's safe, because all list items always extend Number.
List l1TypeErased = l1Double;
List<Number> l1Number = l1TypeErased;
List l2TypeErased = l2Double;
List<Number> l2Number = l2TypeErased;
return myFn(s, l1Number, l2Number);
}
You should see some warnings, that's all. But we know that we get lists of Doubles and that all items of those lists extend Number, so it won't cause trouble at runtime. Just add a comment to explain the type erasure. It's cheaper then copying all values to new lists.