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;
});
Related
This question already has answers here:
List<? extends MyType>
(5 answers)
Closed 6 years ago.
I was trying some sample programs on Generics Upper/Lower bounds.. Generics Upper Bound is giving compilation error...But Lower Bound is fine.
I am just trying to put a List of type T into a set and try both Upper and Lower bound scenarios..
Please help to identify the issue with testUpperBound(T t) method and why exactly does the testLowerBound(T t) method compile and the testUpperBound(T t) one doesn't. I checked other similar threads..But still I haven't got it clear.
Please Let me know if need more details .
public class TestGenerics<T>
{
public static void main(String...args)
{
List<String> list = new ArrayList<>();
list.add("New ArrayList");
new TestGenerics<List<String>>().testUpperBound(list);
new TestGenerics<List<String>>().testLowerBound(list);
}
public void testLowerBound(T t)
{
Set<? super ArrayList<T>> lowerBoundSet = new HashSet<>();
lowerBoundSet = new HashSet<List<T>>();
ArrayList<T> list = new ArrayList<>();
list.add(t);
lowerBoundSet.add(list); // compiles..
out.println(lowerBoundSet);
}
public void testUpperBound(T t)
{
Set<? extends List<T>> upperBoundSet = new HashSet<>();
upperBoundSet = new HashSet<List<T>>();
ArrayList<T> list = new ArrayList<>();
list.add(t);
upperBoundSet.add(list); // Doesn't compile..
out.println(upperBoundSet);
}
}
Here you have your answer :
Explanation of the get-put principle
It is java rule, that's all.
I may give you an example with your code why it would be unsafe if compilation passed :
public void extendsExample(){
Set<? extends List<? extends String>> setOfList = new HashSet<>();
Set<ArrayList<String>> setOfArrayList = new HashSet<>();
// now setOfList var refers to a set
// which contains a arraylist of String
setOfList = setOfArrayList;
// compilation fails
setOfList.add(new LinkedList<String>());
}
Imagine the compilation doesn't fail.
It means setOfArrayList instance which is a set which should contain ArrayList instances, contains now a list of LinkedList element.
If you iterate on setOfArrayList you will not have exclusively ArrayList<String> elements as expected. It's no safe and that's why the compilation fails.
Here the example with <? super :
public void superExample(){
Set<? super ArrayList<String>> setOfArrayList = new HashSet<>();
// compilation ok
setOfArrayList.add(new ArrayList<String>());
// new anonymous type derivating from ArrayList
ArrayList<String> derivedArrayList = new ArrayList<String>(){
};
// compilation ok
setOfArrayList.add(derivedArrayList);
}
You can not modify a collection parameterized with <? extends SomeType>. Java just does not allow that as it is not safe action. add() modifies the collection, so you can't do it. There is no such restriction for <? super SomeType>
Simply put, we do not know at compile time what type of lists are contained in the upperBoundSet. It could be a Set<ArrayList<T>>, or it could be a Set<LinkedList<T>>, or it could be one of many other alternatives.
If it turns out to be a Set<LinkedList<T>>, then adding an ArrayList to it is obviously a bad idea. But because we don't know what type the set's contents are, it takes the safer option and blocks it.
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
I'm a beginner in Java and I'm having a hard time understanding the wildcard generic types. Can anyone help me understand why the following two lines are incorrect?
LinkedList<? extends List> list1 = new LinkedList<ArrayList>();
LinkedList<? super List> list2 = new LinkedList<Collection>();
When I tried to compile them, I got the following error message:
Test.java:13: error: incompatible types: LinkedList<ArrayList> cannot be converted to LinkedList<? extends List>
LinkedList<? extends List> list1 = new LinkedList<ArrayList>();
^
Test.java:14: error: incompatible types: LinkedList<Collection> cannot be converted to LinkedList<? super List>
LinkedList<? super List> list2 = new LinkedList<Collection>();
^
2 errors
if we break down the first, it might make a bit more sense:
LinkedList<? extends List> list1
you declare list1 to be a LinkedList whose element type can be compatable with any subclass of List.
list1 = new LinkedList<ArrayList>();
you then try to instantiate list1 as a LinkedList whose element type is restricted to ArrayList. That's probably why the compiler complains about the incompatable types for that first line. You need to make sure the generic type for the declaration and instantiation of List1 are compatable, like so:
LinkedList<? extends List> list1 = new LinkedList<? extends List>();
I was mistaken. The compiler throws an error for the above line. This line does work though and is probably what you want:
LinkedList<List> list1 = new LinkedList<List>();
check out example code here
As of java 7, you can use the diamond operator to infer the generic type in the instantiation part:
list1 = new LinkedList<>();
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.
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.