List<interfaceI> vs List<? extends InterfaceI> in java - java

Arraylist with List<interfaceI> and List<? extends InterfaceI> both will have objects of classes implementing interfaceI. Then when what should be used?

Suppose Foo and Bar are two classes implementing InterfaceI.
The second one (List<? extends InterfaceI>) doesn't allow adding anything to the list (except null), since the type that the list contains is unknown: it could be a List<Foo> or a List<Bar>: you just don't know.
So you usually use this notation for a method argument, when you want the method to read the elements of the list passed as argument, and want the caller to be able to call your method with a List<InterfaceI>, a List<Foo> or a List<Bar>. Using List<InterfaceI> as argument would only accept lists of type List<InterfaceI>.
Let's take a concrete example: you want to compute the maximum double value of a list of numbers. Such a method doesn't need to add or set anything to the list. All it does is iterating on the elements, get each number and compute their maximum. The signature could be
public double max(List<Number> list);
But then, you won't be able to do
List<Integer> ints = new ArrayList<>();
max(ints);
The only way to call this method is to do
List<Number> ints = new ArrayList<>();
max(ints);
Whereas if you declare the method as
public double max(List<? extends Number> list);
then you can do
List<Integer> ints = new ArrayList<>();
List<Long> longs = new ArrayList<>();
max(ints);
max(longs)

The difference is that if you declare your list as List<? extends S> myList, the myList variable can by list of any type that extends S so associations like shown below will work:
public class Clazz implements S{}
List<? extends S> myList = new List<Clazz>(); // its fine as Clazz extends S
List<? extends S> myList = new List<S>(); // its fine as well
List<? extends S> myList = new List<Object>(); // ooooops it wil not work
But in such case you will not be abe to PUT anything to such list as you cannot guarantee the exact type of object that is held by list implementation assigned to myList
If you declare List<S> myList than you will be able to PUT and GET objects from list, as you are sure what is in it, however assignments from above will not work!
public class Clazz implements S{}
List<S> myList = new List<Clazz>(); // no way!
List<S> myList = new List<S>(); //thats perfectly fine! - You can PUT new S in it;
List<S> myList = new List<Object>(); //known result;

here in place of this
public double max(List<? extends Number> list){
if you do like-
public List myList(List<? extends String> list){
list.add("string");// No, can not add anything in list except null
list.add(null);
}

Related

Java Generics not able to add child to List of parent

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);
}
}

Comparable VS <? extends Comparable>

regarding the following code:
public class Test <T extends Comparable>{
public static void main(String[] args){
List<String> lst = Array.asList("abc","def");
System.out.println(func(lst));
}
public static boolean func(List<**here**> lst){
return lst.get(0).compareTo(lst.get(1)) == 0;
}
}
why writing " ? extends Comparable" here would compile , and writing "Comparable" would not compile?
thanks in advance.
This happens because generics are invariant. Even if String is a Comparable, meaning:
String s = "";
Comparable c = s; // would work
Generics of these would not work:
List<Comparable> listC = List.of();
List<String> listS = List.of();
listC = listS; // will fail
And this would not work no matter what is the relationship between Comparable and String.
When you change the definition of that method to:
public static boolean func(List<? extends Comparable> lst) {
...
}
This is said that: wildcard with an extends-bound makes the type covariant.
This means that :
List<? extends Comparable> listC = List.of();
List<String> listS = List.of();
listC = listS; // would work here
Or in simpler words it means that List<String> is a subtype of List<? extends Comparable>.
There is a small price to pay now, because listC is now a producer of elements, meaning you can take elements out of it, but you can not put anything into it.
And well after you understand this, you are not done yet, because the definition of that method would be entirely correct, when written like this:
public static <T extends Comparable<? super T>> boolean func(List<T> lst) {
.....
}
This is because List<SubClass> cannot be cast to List<BaseClass>. Let us suppose that it could
List<String> sList = new ArrayList<>();
List<Comparable> cList = (List<Comparable>) sList;
cList.add(5);
This would be a problem because the integer 5 is not a String and should not be placed in a List<String>.
By using ? extends Comparable you are saying that the function can take a list of anything that has Comparable as a base class (e.g. List<Comparable>, List<String>, List<Integer>, etc.)
To more correctly define your function you should do the following:
public static <T extends Comparable<T>> boolean func(List<T> lst) {}
This enforces that the type is comparable with itself. Your function compares the first and second element in the list, so it is best to ensure that each element in the list is actually comparable with every other element.
Because
List<String> lst = Array.asList("abc","def");
lst list has generic type String, not Comparable.
String class, hovewer, implement Comparable<String> interface, so it fits in ? extends Comparable generic type.

Unsure how to write method in Java like template in c

I want to implement a function with signature
static boolean checkAnyOutOfBoundary(int[] index,ArrayList<ArrayList<T>> list)
Where I want to substitute T with other self-created class(DocScore). I tried to substitute T with Object. But when I try to instantiate the list instance, like
ArrayList<ArrayList<Object>> DocScoreList = new ArrayList<ArrayList<Object>>();
for (int i = 0; i < index.length; i++)
DocScoreList.add(root.children.get(i).docList);
The type of root.children.get(i).docList is ArrayList<DocScore>
I can not add element into DocScoreList. Does anybody know how can I implement this?
So you are adding an ArrayList<DocScore> to a ArrayList<ArrayList<Object>> but the add method expects an ArrayList<Object>.
Can you provide an ArrayList<DocStore> where ArrayList<Object> is required? No, because A bag of banana is not a bag of fruit
So DocScoreList has to be an ArrayList of something to which a ArrayList<DocStore> can be added. Some examples are:
ArrayList<DocStore> actual type
List<DocStore> because ArrayList<T> is a List<T>
ArrayList<? extends Object> a list of some unknown type that (directly or indirectly) extends Object. Since DocStore meets the criteria ? extends Object this will work.
List<? extends Object>
etc. You get the idea.
static <T> boolean checkAnyOutOfBoundary(int[] index,ArrayList<ArrayList<T>> list)
Example of calling it:
boolean b = checkAnyOutOfBoundary( new int[]{0,1}, new ArrayList<ArrayList<String>>());
Because even in the case that Bar is a subclass of Foo, an ArryList is not a superclass of ArrayList.
Change
ArrayList<ArrayList<Object>> DocScoreList = new ArrayList<ArrayList<Object>>();
to
ArrayList<ArrayList<? extends Object>> DocScoreList = new ArrayList<ArrayList<? extends Object>>();

Java Generics -- Assigning a list of subclass to a list of superclass

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.

How can I add to List<? extends Number> data structures?

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.

Categories