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 :)
Related
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.
When I try to compile the following code:
LinkedList<List<? extends Number>> numList = new LinkedList<List<Integer>>();
I get an incompatible type error:
Required: LinkedList <java.util.list<? extends java.lang.Number>>
Found: LinkedList <java.util.list<Integer>>
How can I achieve having a LinkedList which contains elements that are Lists with elements that extend Number?
To be clear, I'm looking to add lists to numList in the following fashion:
numList.add(new LinkedList<Integer>());
Wildcard capture does not go more than one generic level deep. So while this works:
LinkedList<? extends Number> test = new LinkedList<Integer>();
This does not:
LinkedList<List<? extends Number>> numList = new LinkedList<List<Integer>>();
The most reasonable explanation I can think of is to think of generics as invariant at the outermost level. A LinkedList<List<Integer>> is not a LinkedList<List<? extends Number>>, even though a List<Integer> is a List<? extends Number>, for the same reason that a List<Dog> is not a List<Animal> even though a Dog is an Animal. Here, Dog is to Animal as List<Integer> is to List<? extends Number>.
Well, the Dog/Animal solution is ? extends:
List<? extends Animal> animals = new List<Dog>();
Applying the same reasoning, the workaround is another ? extends:
LinkedList<? extends List<? extends Number>> numList = new LinkedList<List<Integer>>();
However, you won't be able to add anything to this list because of the first ? extends. The reference type variable numList doesn't know which subtype of List<? extends Number> it really is; it could be ArrayList<Integer>, so Java cannot provide the type safety that such a thing can be added to such a LinkedList. To maintain type safety, the compiler will only allow adding null. You'll have to match the generic type parameters exactly, with no wildcards: LinkedList<List<Integer>> numList = new LinkedList<List<Integer>>();. You can add a List<Integer> to such a LinkedList.
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.
I have a basic question regarding assignment of a list of subclass to a list of superclass.
So I have something like the following:
Class B extends A;
List <B> bList = new ArrayList<B>();
List <A> aList = bList;
Why does this last assignment fail? Sorry for the newbie question
To explain this, let me substitute "B" with Integer and "A" with Number. This is just to make it a little easier to explain.
Class Integer extends Number;
List <Integer> iList = new ArrayList<Integer>();
List <Number> nList = iList // will fail
The reason this would fail is because nList can take any Number -- it can take Integer, it can take Double, or for that matter any subclass of Number. However, this is not true for iList. You cannot add a Double to iList because it accepts only Integer and its subclasses. Hope this helps explain it to you.
When you declare a List of items of type A, only items of type A can be added or removed from the List. If you need to include subclasses of A, use the generic wildcard ? extends A to indicate so. Your code should therefore be:
List <? extends A> aList = bList;
List<B> is not List<A>:
Through example: let say you have class B1 extends A{} and class B2 extends A{}
then (if you would be able to do that:
List<B1> b1 = new AList<B1>();
List<A> a = b1;
List<B2> b2 = new AList<B2>();
by the hypothesis, you should be able to do
a.add(new B2())
but this is wrong.
If you try the same thing but using arrays instead of lists, it will compile and throw exception in runtime.
We say that arrays are covariant and generics are invariant.
to make the code compile you have the wite it:
List<? extends A> a = b;
this says that a is a list of some subtype of A. _But you don know which one. Because of that you can't do a.put(X)
List<B> and List<A> are invariant type. What you need is covariant type. In this case, it is List<? extends A>.
Because generics are strict type safe.
You can have
List<? extends A> aList = bList;
It says aList can hold list of any type which is an A
Because List<B> does not extend List<A>. For example, Integer extends Number and so does Long. So List<Number> can contain both Integer and Long. So if you assign List<Integer> to List<Number> you will be able to add Long to your list of integers.
You can declare
List<? super B> superB;
And that would allow assignment to superB of any list that contains B and its super classes.
But it's not the same as in your case aList=bList.
or
List<? extends A> extendsA;
Examples
List<? super Integer> superA;
superA = new ArrayList<Number>();
List<? extends Number> extendsNumber;
extendsNumber = new ArrayList<Integer>();
While at first glance you might think that
Class B extends A;
List <B> bList = new ArrayList<B>();
List <A> aList = bList;
should work, the problem is obvious when you imagine actually using these lists:
A something = new A();
aList.add( something ); // Should work because aList is a list of A's
but aList was assigned to bList, so that should be the same as
bList.add( something ); // Here's the problem
bList.add() takes a B, but something is an A, and an A is not a B!
And that's why generics should be (and are) strict type safe.
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.