I am reading book for OCP of Author Jeanne Boyarsky and Scott Selikoff, Book saying : Page # 122
? super String
With a lower bound, we are telling Java that the list will be a list of String objects or a
list of some objects that are a superclass of String
List<? super String> superString = new ArrayList<>();
superString.add(new String());// Valid
superString.add(new Object()); // Not Valid ? Why?. Object is a super class of String
Other Example:
List<? super IOException> exceptions = new ArrayList<Exception>();
exceptions.add(new Exception()); // Invalid, why ? Exception is a superclass of IOException
exceptions.add(new IOException());
exceptions.add(new FileNotFoundException());
I think this statement
With a lower bound, we are telling Java that the list will be a list of String objects or a
list of some objects that are a superclass of String
Should be
With a lower bound, we are telling Java that the list will be a list of String objects or a
list of some objects whoes super class is String
if this case is true then why we use
List<? super IOException> exceptions = new ArrayList<Exception>();
instead of
List<? super IOException> exceptions = new ArrayList<IOException>();
You need to understand, that these bounds (lower and upper) exist for restricting/specifying the variable's type, and not for restricting/specifying the type of elements inside such a collection.
An example:
List<? super String> variable = new ArrayList<CharSequence>();
With this statement, you first create an ArrayList whose elements can be of type CharSequence or a subtype of it (e.g. String or StringBuilder).
With that declaration it should be clear that you can not add an Object into this list. It simply does not implement the CharSequence interface. The compiler takes care of that.
Lower and upper bounds exist for making subtypes of generic types. Their use is explained in this question about PECS.
In fact, a List<? super String> is a list with a concrete element's type, but this concrete type is not known at this moment. See my example initialisation. The concrete type is the CharSequence. It simply means that all elements are of that concrete type (or a subtype), but that is not known in the variable's own type.
Here's a link to a good explanation Difference between <? super T> and <? extends T> in Java
The book is correct "? super String" means that the list may contain Strings or a supertype of String (eg. Object, CharSequence)
In you example:
List<? super String> superString = new ArrayList<>();
superString.add(new String());// 1
superString.add(new Object()); // 2
Write operations:
1 - is valid for any type of list you could've created , since String is an Object, a CharSequence...
List<? super String> list = new ArrayList<Object>();
list.add("String");
2 - not valid because it doesn't cover all cases
List<? super String> list = new ArrayList<String>();
list.add(new Object());
Given the declared type List<? super String> you can only add Strings (and String subtypes) , anything less than a String (supertypes) may not correspond to the actual element type.
Example:
Here's an example :
interface MyInterface {
}
class MyInterfaceImpl implements MyInterface {
}
class MyInterfaceImplExtra extends MyInterfaceImpl {
}
You may have the following situations:
List<? super MyInterfaceImpl> myList = new ArrayList<MyInterfaceImpl>();
Doesn't compile because myList variable may point to a list of either MyInterfaceImpl or MyInterface or Object. When adding to the list it's unclear exactly what kind of list you actually have, thus you are allowed only values that are applicable for all cases.
myList.add(new MyInterface(){}); // - compile error "not applicable for the arguments"
myList.add(new MyInterfaceImpl()); // - works fine
myList.add(new MyInterfaceImplExtra()); // - works fine
An example where you get a list of values. The element type is Object , a more concrete type can't be guaranteed. For example here you expect Strings but get a List<? super String> that actually contains Objects so you get a java.lang.ClassCastException
List<String> result = (List<String>) getList();
System.out.println(result.get(0));
public static List<? super String> getList(){
List<Object> list = new ArrayList<Object>();
list.add(new Object());
return list;
}
Usage example in Java 8 :
You have the following method declaration in the Stream interface that restricts the predicate type to be T or supertype.
Stream<T> filter(Predicate<? super T> predicate);
This ensures that given a class Person that defines name and Employee that extends Person and defines an extra field id , you can't do something like this:
List<Person> list = new ArrayList<Person>() {{
add(new Person("John"));
add(new Person("Max"));
add(new Person("Megan"));
}};
list.stream().filter((Employee e) -> e.getId().startsWith("1")); // compile error
Related
I have the following Java code:
List<? extends Collection<String>> attributes = new ArrayList<HashSet<String>>(nrAttributes);
...
attributes.replaceAll((in) -> {
List<String> out = new ArrayList<>();
out.addAll(in);
return out;
});
But it gives me a compiler exception (last out is red) saying:
Type mismatch: cannot convert from List to capture#2-of ? extends Collection
Your extends just ensures you that you will read a Collection<String> from your list. But you can't write it.
For a very good explanation see this link where i took the following from:
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.
Command line says:
J:\WS\Java test>javac Main.java -Xdiags:verbose
Main.java:11: error: method replaceAll in interface List<E> cannot be applied to given types;
attributes.replaceAll((in) -> {
^
required: UnaryOperator<CAP#1>
found: (in)->{ Li[...]ut; }
reason: argument mismatch; bad return type in lambda expression
List<String> cannot be converted to CAP#1
where E is a type-variable:
E extends Object declared in interface List
where CAP#1 is a fresh type-variable:
CAP#1 extends Collection<String> from capture of ? extends Collection<String>
1 error
i.e. the ? capture, might be a sub-type or sibling of List<String>, so it is unsafe to add a List<String> to a List<? extends Collection>. The ...extends Collection doesn't really matter here.
Possible solution:
List<? extends Collection<String>> attributes = new ArrayList<HashSet<String>>(nrAttributes);
...
List<List<String>> attribs2 = new ArrayList<>();
attributes.forEach((in) -> {
List<String> out = new ArrayList<>();
out.addAll(in);
attribs2.add(out);
});
Or:
List<Collection<String>> attributes = new ArrayList<>(nrAttributes);
...
attributes.replaceAll((in) -> {
List<String> out = new ArrayList<>();
out.addAll(in);
return out;
});
You defined your attributes variable as a list of something that extends a collection of strings. At compile-time, the compiler cannot decide if attributes is in fact an instance of List<TreeSet<String>, or a List<LinkedList<String> or even a List<Vector<String>. Consequently, the compiler does not allow you to modify the attributes collection because there is a risk that you are adding an ArrayList<String> to a collection of another collection of strings (say TreeSet<String>.
In general, if you have a List<? extends Something>, the compiler wont allow you to modify the collection (add/remove/clear/...). Any list typed with ? extends T is read-only !
Compiling :
List<Collection<String>> attributes = new ArrayList<>(nrAttributes);
When you say List> you are saying "A List of a particular subtype of Collection"
All such ? extends X are named internally as capture#n of ? extends X (the first time it's capture#1..., the second capture#2... and so on), but every time it's considered a particular type. For instance, the following code does not compile either, with the same error
Why do you need the generics for? the following example can contain any subclass of Collection of String (compiles, btw)
List<? extends Collection<String>> attributes = new ArrayList<HashSet<String>>(nrAttributes);
attributes.replaceAll((in) -> {
List<String> out = new ArrayList<>();
out.addAll(in);
return out;
});
List<? extends String> list = new Arraylist<String>();
list.add("foo");
Given piece of code gives me compile time error.i don't get it why i can't add string in list.
but the code means that we can add the String class object and it's derived class object in the list
still i am getting the error why
List<?> should only be used when you are not concerned with the data type of the items and interested in operations such as getting size of list etc.
For Example,
public int getSize(List<?> itemList) {
return itemList.size();
}
It is more of a Read Only list.
You should be using the following if you intend to make a new list of String items.
List<String> list = new Arraylist<>();
list.add("foo");
Alternatively, you can use this:
List<Object> list = new Arraylist<>();
list.add("foo");
This will work:
List<? super String> list = new ArrayList<String>();
list.add("foo");
Then your compiler will now, that the caller is to pass a list of objects that are String or a super type.
When you say <? extends String> it means it can be of any type which extends String. That means somebody can pass List and it will accept it.
Look also here:
Difference for <? super/extends String> in method and variable declaration
Essentially List<? extends String> is a List of an unknown type, all that is known is that this type extends String. It does not matter that the variable contains an instance of List<String>.
Now String is final, so there is no subclass... so let's consider:
List<? extends A> list = new ArrayList<A>();
where A is some class with a subclass B. When used, a variable of type List<? extends A> might be an instance of List<B>. We can not add A to it because there is the possibility that an A is not a B.
But you know it will be right? Well, the compiler doesn't, nor does it anticipate the logical connections between the unknown type and what you've instantiated, because in general you may change that variable at runtime.
A similar example exist in the tutorial optimistically named More Fun with Wildcards, where a method accepts (Set<T>, T) and an illegal call is preposed using (Set<?>, String) though the Set<?> variable contains an instance of Set<String>. The issue is the same here, despite the addition of extends.
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.
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.
Consider this code:
public <T> List<T> meth(List<?> type)
{
System.out.println(type); // 1
return new ArrayList<String>(); // 2
}
It does not compile at line 2, saying that List is required.
Now, if it's changed to:
public <T> List<?> meth(List<T> type)
{
System.out.println(type); // 1
return new ArrayList<String>(); // 2
}
It does compile. Why? I thought the difference between declaring a generic type with T and using the wildcard was that, when using the wildcard, one cannot add new elements to a collection. Why would <?> allow a subtype of List to be returned? I'm missing something here, what's the explicit rule and how it's being applied?
The difference is in the return type declaration. List<String> is not a subtype of List<T>, but it is a subtype of List<?>.
List<?> makes no assumptions regarding its type variable, so the following statements are valid:
List<?> l0 = new ArrayList<String>();
List<?> l1 = new ArrayList<Object>();
List<? extends Number> ltemp = null;
List<?> l2 = ltemp;
List<T> assumes that the type argument will be resolved in the context of client (e.g. type use), when you declared it as List<String> or List<Object>. Within the method body, you cannot make any assumptions about it either.
In the first case, T is not necessarily a superclass of String. If you choose a T like Integer and call the method, it'll fail; so it won't compile. However, the second will compile as surely, any ArrayList<String> is a valid List of something.
As said earlier, String isn't a subtype of T, so it's why it does not work. However, this code works :
public <T> List<T> meth(List<?> type)
{
System.out.println(type); // 1
return new ArrayList<T>(); // 2
}
and is more in the idea of what you want, I think.