Why is List<String> accepting another <List> as an element - java

import java.lang.Math;
import java.util.*;
import java.io.*;
class Hello {
public static void main(String args[]) throws FileNotFoundException{
String[] veri2 = {"No", "Compilation", "Error"};
List<String> veri1 = new ArrayList<String>();
veri1.addAll(Arrays.asList(veri2)); // ---------- 14
System.out.println(veri1+"elements in hashset");
}
}
Why the above code doesnt throw a compile error at line 14 when a List is added to another List whose elemnts are of type String ?

The List<E>.addAll method accepts a Collection<? extends E>, and the List<E> interface inherits from Collection<E>.
If you tried to add a String using addAll, you would actually get an error.
List<String> list = new ArrayList<String>();
list.addAll("Hello");
The above code wouldn't work, since String does not implement Collection<String>.

The signature in your case is addAll(Collection<String> c) and since you pass a List<String> which extends Collection<String> all is fine.

Arrays.asList() is generic and since your array is an array of String, it returns a List<String>, which is exactly what you want.
see: http://download.oracle.com/javase/6/docs/api/java/util/Arrays.html#asList(T...)

Why would you expect a compiler error? The signature of addAll() is:
boolean addAll(Collection<? extends E> c)
So in your case:
boolean addAll(Collection<? extends String> c)
And Arrays.asList():
public static <T> List<T> asList(T... a)
Which means for your
public static List<String> asList(String... a)
So addAll() wants a Collection<? extends String> and gets a List<String> - which is perfectly OK.

Because Arrays.asList() returns a List. In your case it is interpreting your String[] and setting that as the type.

When you add all the elements of a List<String> to a List<String> this is the one occasion you shouldn't get an error.

Because addAll adds each element of a collection to a list. If you had called add, you presumably would have gotten the error you expected.

As you can see in the List API:
public boolean addAll(Collection c)
Appends all of the elements in the specified collection to the end of this list...
So no type error because addAll receives Collection (List is a Collection) as argument and adds them to the list.

Related

How to assign List<? extends BaseClass> to List<BaseClass>

having
class BaseClass implements IData ();
class ChildClassA() extends BaseClass;
class ChildClassB() extends BaseClass;
since cannot do
List<BaseClass> aList = new ArrayList<ChildClassA>()
so there is a
List<? extends IData> aList
for pointint to either
ArrayList<ChildClassA>(),
or
ArrayList<ChildClassB>()
the aList is built by other routing at runtime, and that part of code has a function to take a List<IData> from the the aList
the question is if the List<? extends IData> aList is point to ArrayList<ChildClassA>() or ArrayList<ChildClassB>(),
can it do ListData<IData> outputList = (List<IData>) aList? something like below:
(seems it is working, but not sure if there is better way to assign the generics array other than casting.)
Edit: the output of the List<IData> outputList is for read only use (immutable), no insert/delete on it, it will only iterate the IData to react on what the IData really is.
List<? extends IData> aList = new ArrayList<ChildClassA>();
ListData<IData> outputList = (List<IData>)aList
List<? extends IData> aList = new ArrayList<ChildClassB>();
ListData<IData> outputList = (List<IData>)aList
tl;dr Use Collections#unmodifiableList:
List<IData> outputList = Collections.unmodifiableList(aList);
For more information on this topic, you might want to get familiar with the PECS principle.
It's not possible, because the two types are incompatible.
A List<BaseClass> is just what it is declared, a list of BaseClass objects. More precisely, it makes two guarantees:
objects retrieved from it are assignable to BaseClass
every object that is assignable to BaseClass can be added to it (and no other)
A List<? extends BaseClass> is a more loose declaration. Precisely, it simply does not make the second guarantee. However, not only the guarantee is gone, but it is now impossible to add items to it, since the exact generic type of the list is undefined. It might even change for the same list declaration (not the same list object) at runtime.
As a consequence, a List<? extends BaseClass> is not assignable to a List<BaseClass>, since the latter makes a guarantee the first is unable to fulfill.
Practically speaking, consider the following method:
public List<BaseClass> makeList() {
// TODO implement method
return null;
}
If someone implements this method, returning a List<? extends BaseClass>, a client using this method would be unable to add items to it, although its declaration indicates otherwise.
Because of that, such an assignment results in a compilation error.
To fix the example problem the loose declaration can be added to the method:
public List<? extends BaseClass> makeList() {
// TODO implement method
return null;
}
This will signal every client, that the list returned from this method is not meant for adding items to it.
Now let's get back to your use case. In my opinion the most appropriate fix is to the rephrase the function that
take[s] a List from the the aList.
As it seems it is currently declared as
public void useList(List<BaseClass> list);
but since it does not add items to the list, it should be declared as
public void useList(List<? extends BaseClass> list);
However, if that method is part of a currently unchangeable API, you can still do:
List<? extends BaseClass> list;
....
List<BaseClass> tmp = Collections.unmodifiableList(list);
useList(tmp);
No, it is unsafe.
After that cast it would be legit to add to the list that is supposed to contain only ChildClassA typed elements, element of the other child type ChildClassB type and vice-versa.
We can simply your code a bit to make it more obvious why this should not be allowed:
List<ChildClassA> aList = new ArrayList<ChildClassA>();
aList.add(a1);
aList.add(a2);
//...
List<IData> iDataList = (List<IData>) aList;
iDataList.add(b1);
iDataList.add(b2);
//...
for (ChildClassA a : aList) {
// a some point a is going to be assigned b1 or b2 and they results in cast
// exception.
}
Note that iDataList makes reference to the very same list object as aList.
If that cast was allowed then you would be able to add elements to aList that are not ChildClassA instances.
The best solution is on the details.
If the problem is that a third-party library requires a List<IData> typed reference and as long as it is only for reading you can use a unmodifiable proxy as returned by Collections.unmodifiableList:
import java.util.Collections;
//...
final List<ChildClassA> aList = new ArrayList<>();
//... add stuff to aList
final List<IData> readOnlyIDataList = Collections.unmodifiableList(aList);
//... read only access operations readOnlyIDataList

Java Generics List

Following is an example of Generics with wild card
public static void printListItems(List<object> list) {
for (Object listItem : list)
System.out.println(listItem);
}
In this example we want to print list items of any type but it can’t print List<Integer>, List<String> etc. because they are not subtypes of List<Object>. This problem can be solved using unbounded wildcard.
public static void printListItems(List<?> list) {
for (Object listItem : list)
System.out.println(listItem);
}
I read this above code in Java tutorial. For the first example , it says it cannot work because List<String> is not sublass of List<Object>.
Then why it is so that in the second example the for loop is working with taking listItem as dataType of Object and iterating through List<String> elements.
You are running into a false contradiction because you are equating the relationship between List<String> and List<Object> to the relationship between String and Object.
In the first example, the compiler will yell at you if you attempt to call printListItems with a List<String>, because List<String> does not extend List<Object>.
In other words: A list of strings cannot be treated as a list of objects, because (according to the type system) a list of strings is not a list of objects.
In the second example, the compiler will not yell at you, because you are treating the elements of the List, which are Strings, as Objects. Unlike the case with the lists, String does extend Object.
In other words: A string can be treated as an object, because (according to the type system) a string is an object.
when the type-declaration of generic class contain wildcard character, its subclass type can be extend in two dimension.
E.g. Collection<?extends Number>
its subclass type can be extend in dimension Collection, for List and Set are all subclass type of Collection so List<? extends Number> and Set<? extends Number>are all subclass type of Collection<? extends Number>
its subclass type can be extend in dimension Number, because Integer 、Double are all subclass type of Number so Collection<Double> and Collection<Integer> are all subclass of Collection<? extends Number>
about your second, as Andreas explained in comment, List is equal to List<? extend Object> and based on the second dimension extend List<String> is **subclass** of List<?>
What you've done is not generic. This is a generic function
public static <T> void printListItems(List<T> list) {
for (T listItem : list) {
System.out.println(listItem);
}
}
Example:
import java.util.List;
import java.util.ArrayList;
import java.util.Arrays;
public class Main {
public static <T> void printListItems(List<T> list) {
for (T listItem : list) {
System.out.println(listItem);
}
}
// arguments are passed using the text field below this editor
public static void main(String[] args) {
List<Integer> l = new ArrayList<>(Arrays.asList(1,2,3,4,5));
List<String> s = new ArrayList<>(Arrays.asList("Hello", "World"));
printListItems(l);
printListItems(s);
}
}
Output
1
2
3
4
5
Hello
World

Why can't I add to a List<? extends String> in Java?

In the snippet below, adding "Hulooo" to the list generates a compiler error since String doesnt extend String.
However, typecasting an ArrayList of strings works. But typecasting an ArrayList of objects doesn't work.
Can someone explain why this is happening?
What applies to String(in this context) that doesn't apply to Object?
public static void takeList(List<? extends String> list)
{
list.add("Hulloo");//ERROR
list=new ArrayList<String>();
list=new ArrayList<Object>();//ERROR
}
The question mark ? is a so-called wild-card operator. List<? extends String> means: any type List<T> where T is String or a sub-type of String. String is a final class, and thus there are no sub-types of String, but the compiler does not look at this. And thus, the compiler assumes that the list could be a list of some sub-type of String, and it would not be possible to add Strings to such a list.
Let's simulate your example with a non-final class, A, which has a sub-class, B:
class A {
// empty
}
class B extends A {
void f();
}
And now consider the equivalent to your method:
public static void takeList(List<? extends A> list) {
list.add(new A());
}
This will not compile, for the following reason. Suppose you call this method as follows:
List<B> myList = new ArrayList<>(); // line 1
takeList(myList); // line 2
B element = myList.get(0); // line 3
B.f(); // line 4
Since B is a sub-class of A the function call in line 2 is legal. But the method takeList adds an A to the list which is not a B. An then the list of Bs contains an element which is not a B, and line 3 and 4 break down.
The type system is there to prevent typing errors, so if there is one scenario where you could add an object of the wrong type to a list, the type system must forbid it.

How do I invoke a method that takes a "Collection<? extends T>" parameter?

The method signature looks like this:
public void addThemAll(Collection<? extends T> c)
Which essentially just adds every element of the collection to my LinkedList. But I keep trying to feed this method an Array or a Linked List and I always get an error. For example:
double[] myarray = new double[]{3.4, 4.5, 8.6};
mylist.addThemAll(myarray);
I'm sure this is something straightforward, but I can't find an example online that just passes an array/linked list into a method like this.
Your code has two problems:
An array is not a collection. It does not extend Collection. Therefore, you can't pass it into a method whose signature specifies a collection parameter.
You have not defined <T> (or, at least, you have not shown us where you are defining <T>). You can either define <T> in your class, or in your method signature.
To define it in your class, do it like this:
public class MyClass<T> {
// contents
}
To define <T> in your method, do it like this:
public <T> void addThemAll(Collection<? extends T> c) {
// method logic
}
For what you are doing, this would work:
List<Double> myArray = Arrays.asList(3.4, 4.5, 8.6);
mylist.addThemAll(myarray);
The reason being is that you are passing in a list (which is a collection). Currently you are passing in an Array, which is not a collection.
To pass in the array to collection:
Double[] myarray = new Double[]{3.4, 4.5, 8.6};
mylist.addThemAll(Arrays.asList(myarray));
if you don't want it as list but want it as LinkedList or etc
LinkedList<Double> linkedlist = new LinkedList(Arrays.asList(myarray));
mylist.addThemAll(linkedlist);
if you want to use set or treeset
TreeSet <Double> treeset = new TreeSet(linkedlist);
Difference between set and list is that set does not have duplicate and not ordered, and list is ordered but contains duplicates.
After you pass in to your method:
public void addThemAll(Collection<? extends T> c)
if(c instanceof LinkedList){
LinkedList a = (LinkedList) c //you can invoke methods from LinkedList
....
}

Generic Collection

This is part of the Java (1.6) Collection interface:
public interface Collection<E> extends java.lang.Iterable<E> {
/* ... */
boolean containsAll(java.util.Collection<?> objects);
boolean addAll(java.util.Collection<? extends E> es);
boolean removeAll(java.util.Collection<?> objects);
boolean retainAll(java.util.Collection<?> objects);
/* ... */
}
Why does addAll have <? extends E> while removeAll has <?>
I did not know, I googled. I got this explaination here: http://www.ibm.com/developerworks/java/library/j-jtp01255/index.html
Copying the part:
One element of the generifed Collections API that is often confusing at first is the signatures of containsAll(), removeAll(), and retainAll(). You might expect the signatures for remove() and removeAll() to be:
interface Collection<E> {
public boolean remove(E e); // not really
public void removeAll(Collection<? extends E> c); // not really
}
But it is in fact:
interface Collection<E> {
public boolean remove(Object o);
public void removeAll(Collection<?> c);
}
Why is this? Again, the answer lies in backward compatibility. The interface contract of x.remove(o) means "if o is contained in x, remove it; otherwise, do nothing." If x is a generic collection, o does not have to be type-compatible with the type parameter of x. If removeAll() were generified to only be callable if its argument was type-compatible (Collection<? extends E>), then certain sequences of code that were legal before generics would become illegal, like this one:
// a collection of Integers
Collection c = new HashSet();
// a collection of Objects
Collection r = new HashSet();
c.removeAll(r);
If the above fragment were generified in the obvious way (making c a Collection<Integer> and r a Collection<Object>), then the code above would not compile if the signature of removeAll() required its argument to be a Collection<? extends E>, instead of being a no-op. One of the key goals of generifying the class libraries was to not break or change the semantics of existing code, so remove(), removeAll(), retainAll(), and containsAll() had to be defined with a weaker type constraint than they might have had they been redesigned from scratch for generics.
For any collection containing elements of type E, addAll must be able to deal with input collections not just of E, but all of its subclasses as well. Hence <? extends E>. Without this, you could not add all elements of a List<Integer> to a List<Number>, which would clearly not be right.*
For removal, the limits need not be so strictly set, and there is no harm in trying to remove elements of a collection of some totally unrelated type. E.g. you can have a collection of Numbers, about which you happen to know that it only contains Integers, so passing it to removeAll on a List<Integer> should work fine, and it would be stupid for the compiler to disallow this.
Note that according to the Javadoc, removeAll may optionally throw a ClassCastException, depending on implementation.
*The reason behind this is that in Java, generics are invariant. For more details, see e.g. this thread.
<?> is less restrictive than <? extends E>.
There is nothing wrong with removing an orange from a collection of apples; there are a lot of things wrong with adding an orange to a collection of apples.
When you add item to your collection you want to be sure that they do have a certain type.
When you remove them, only those in the collection are removed. Regardless of their type.
Java implements generics through erasure. These info are only for compilation time only. I guess the java collection designers did this to retain more ascendent compatibility with pre-generics java version.
when you add an object, it needs to be a subclass (or sub-subclass, etc.) of the main type. When you remove an object, it returns it as the type oc the collection. This is a good example of polymorphism in action.
A simple example to illustrate what has been said:
public class Test {
public static void main(String[] args) {
List<String> l = new ArrayList<String>();
System.out.println(l.remove(new Object())); //false
System.out.println(l.contains(new Object())); //false
// l.add(new Object()); // does not compile
}
}
Who cares what you try to remove ?
Adding is something else; we wouldn't want to end up with something strange in our collection.
as requested; an example:
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
public class Main {
private static class A {
}
public static void main(String[] args) {
Collection<A> collection_A = new ArrayList<A>();
Collection<String> collection = new ArrayList<String>();
// no problem to try and remove things that wouldn't be there in the first place; either way, they are gone afterwards
collection.removeAll(collection_A);
// we can't allow this; you would end up with things in your collection that don't belong there
collection.addAll(collection_A);
}
}
To remove restriction is not needed, so only <?>, but while adding we have to check and then add for type safety, so addAll is with restriction <? extends E>
With addAll you want to be able to add all elements that are a subtype of the generic type. This includes adding all elements of a List<String> to a List<Object>. We use ? extends E to accept any Collection that contains the type stored in this collection or any subtype.
boolean addAll(java.util.Collection<? extends E> es);
List<Number> numbers = ...;
List<Integer> integers = ...;
numbers.addAll(integers);//works
boolean addAll(java.util.Collection<E> es);
numbers.addAll(integers);//does not work E != Integer
we can't use ? as that would remove any security provided by generics.
boolean addAll(java.util.Collection<? extends E> es);
List<Number> numbers = ...;
List<Integer> integers = ...;
List<String> strings = ...;
numbers.addAll(integers);//works
numbers.addAll(strings);//error
boolean addAll(java.util.Collection<?> es);
numbers.addAll(strings);//works - now we have strings in our Number collection
We can use ? to remove objects since trying to remove a String from List of Numbers wont affect a List<Number>.
boolean removeAll(java.util.Collection<?> objects);
List<Objects> objects = ...;
List<Integer> integers = ...;
List<Number> numbers = ...;
numbers.removeAll(objects);//works
numbers.removeAll(integers);//works
boolean removeAll(java.util.Collection<? extends E> objects);
numbers.removeAll(objects);//does not work
numbers.removeAll(integers);//works
boolean removeAll(java.util.Collection<? super E> objects);
numbers.removeAll(objects);//works
numbers.removeAll(integers);//does not work

Categories

Resources