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.
Related
This question already has an answer here:
Difference between LinkedList<?> and LinkedList<Object> [duplicate]
(1 answer)
Closed 4 years ago.
(Note: this is not the same as this question about vs. !)
I'm confused by Java's generics.
ArrayList<? extends Object> x = new ArrayList<String>();
ArrayList<Object> y = new ArrayList<String>();
In this example, the first line compiles while the second one doesn't. Why is that?
I thought an assignment of the shape List<A> x = new ArrayList<B>() should be valid as long as B extends A, i.e. the right side has higher specificity than the left side of the assignment, but apparently I'm mistaken.
Could somebody elaborate on the similarities and differences of these statements?
In Java, the type arguments must match exactly.
Consider the following code snippet:
ArrayList<Object> list = new ArrayList<String>();
list.add(new SomeClass());
We have now succeeded in adding a non-string to ArrayList<String>. There is nothing wrong with the second statement: we're adding some object to an ArrayList<Object>. So there must be something wrong with the first statement! That's why Java does not let you do that.
If you change the snippet to:
ArrayList<? extends Object> list = new ArrayList<String>();
list.add(new SomeClass());
you will notice that the error is now in the second line. You cannot add an object to the list.
The difference between the two is the following:
ArrayList<Object> means "an ArrayList containing Objects". Such a list can of course also contain instances of subclasses of Object. Since every object is an instance of Object, this list can contain any object (in this case).
ArrayList<? extends Object> means "an ArrayList containing instances of some unknown subclass of Object". Such an ArrayList cannot contain every object. Only instances of this unknown subclass can be added to it. Since the specific subclass is not known, it is not possible to add elements to it here. (But elsewhere, the same list may be known as an ArrayList<String>, and there it is of course possible to add elements to it.)
Both below cases won't work because you are initializing different object type than expected:
ArrayList<Object> y = new ArrayList<String>();
ArrayList<String> y2 = new ArrayList<Object>();
If you use ? wildcard you get some more options to have different, but related by inheritance types (String extends Object implicitly):
ArrayList<? extends Object> x = new ArrayList<String>();
Consider also using the diamond operator
ArrayList<Object> y = new ArrayList<>();
You can replace the type arguments required to invoke the constructor of a generic class with an empty set of type parameters (<>) as long as the compiler can infer the type arguments from the context. This pair of angle brackets is informally called the diamond.
String abc = "abc";
Object x = abc;
works but
List<Object> list = new ArrayList<String>();
does not because Generic expressions have to match exactly if they do not contain a wildcard.
Please note that on the declaration side it is better to use List (and not ArrayList) because it allows you to change the implementation at a later stage, e.g. to LinkedList.
I've read a few topics which cover certain questions about generics, such as their relationship with raw types. But I'd like an additional explanation on a certain line found in the Java SE tutorial on unbound generics .
According to a sentence :
The goal of printList is to print a list of any type, but it fails to achieve that goal — it prints only a list of Object instances; it cannot print List<Integer>, List<String>, List<Double>, and so on, because they are not subtypes of List<Object>.
If I understand well this sentence; the difference between List<?> and List<Object>, is that we can use the type argument List<String> or List<Integer> by implementing the former. While if we implement the later, we can only use the type argument List<Object>. As if List<?> is an upper bound to Object namely List<? extends Object>.
But then the following sentence confuses me, in the sense that according to what I previously understood, List<Object> should only contain instances of the class Object and not something else.
It's important to note that List<Object> and List<?> are not the same. You can insert an Object, or any subtype of Object, into a List<Object>. But you can only insert null into a List<?>.
There are two separate issues here. A List<Object> can in fact take any object as you say. A List<Number> can take at least Number objects, or of course any subclasses, like Integer.
However a method like this:
public void print(List<Number> list);
will actually only take a List which is exactly List<Number>. It will not take any list which is declared List<Integer>.
So the difference is List<?> will take any List with whatever declaration, but List<Object> will only take something that was declared as List<Object>, nothing else.
The last quote simply states, that List<?> is a list for which you literally don't know what type its items are. Because of that, you can not add anything to it other than null.
The sentence that is confusing you is trying to warn you that, while List<?> is the super-type of all generic lists, you cannot add anything to a List<?> collection.
Suppose you tried the following code:
private static void addObjectToList1(final List<?> aList, final Object o ) {
aList.add(o);
}
private static void addObjectToList2(final List<Object> aList, final Object o ) {
aList.add(o);
}
private static <T> void addObjectToList3(final List<T> aList, final T o ) {
aList.add(o);
}
public static void main(String[] args) {
List<String> testList = new ArrayList<String>();
String s = "Add me!";
addObjectToList1(testList, s);
addObjectToList2(testList, s);
addObjectToList3(testList, s);
}
addObjectToList1 doesn't compile, because you cannot add anything except null to a List<?>. (That's what the sentence is trying to tell you.)
addObjectToList2 compiles, but the call to it in main() doesn't compile, because List<Object> is not a super type of List<String>.
addObjectToList3 both compiles and the call works. This is the way to add elements to a generic list.
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 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;
});
The error:
The method add(capture#1-of ?) in the type List<capture#1-of ?> is not
applicable for the arguments (String)
Code:
List<?> to = new ArrayList<Object>();
to.add(new String("here"));
Since List<?> is a generic type List and therefore can be of any type, then why it is not accepting String in add method?
A List<?> is a list of some type, which is unknown. So you can't add anything to it except null without breaking the type-safety of the list:
List<Integer> intList = new ArrayList<>();
List<?> unknownTypeList = intList;
unknownTypeList.add("hello"); // doesn't compile, now you should see why
should a String not be acceptable ?
No. <?> means the type is unknown and the compiler cannot be sure that any type is acceptable to add (this include String)
You can specify a lower bound:
List<? super Object> to = new ArrayList<Object>();
to.add(new String("here")); // This compiles
Now the compiler is sure that the list can contain any Object
According to wildcards, the question mark (?), called the wildcard, represents an unknown type and not a generic type, since its an unknown type the compiler cannot accept String in your case.
You can consider any list defined using wildcards to be read only. Still, there are some non-read operations that you can do.
From the docs:
You can add null.
You can invoke clear.
You can get the iterator and invoke remove.
You can capture the wildcard and write elements that you've read from the list.
List<?> could be rewritten as List<? extends Object> and ? extends Object means anything is subclass of Object.
For clarity, assuming we only have two subclass of Object here like String and Integer.
List<? extends Object> means this list can store a list of String such as List<String> or a list of Integer such as List<Integer>, but not a list of mixed String and Integer.
Back to your example, the list to can be a list of String or a list of Integer. When you try to add String into the list, it means this list can only store a list of String. However, the complier know that this list can also be a list of Integer and complier doesn't know which one is the case and an error is showed.
List<?> to = new ArrayList<Object>();
to.add(new String("here"));