Paul McKenzie
Paul McKenzie

Reputation: 20094

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 <?>

Upvotes: 13

Views: 1406

Answers (10)

josefx
josefx

Reputation: 15656

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

Upvotes: 0

Chandra Sekhar
Chandra Sekhar

Reputation: 19500

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>

Upvotes: 0

P&#233;ter T&#246;r&#246;k
P&#233;ter T&#246;r&#246;k

Reputation: 116306

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.

Upvotes: 7

Nishant
Nishant

Reputation: 55886

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.

Upvotes: 12

Tom
Tom

Reputation: 4180

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

}

Upvotes: 0

assylias
assylias

Reputation: 328913

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
    }

}

Upvotes: 0

SLaks
SLaks

Reputation: 888223

<?> 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.

Upvotes: 3

DerMike
DerMike

Reputation: 16200

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.

Upvotes: 2

Scott M.
Scott M.

Reputation: 7347

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.

Upvotes: 0

Nicocube
Nicocube

Reputation: 2992

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.

Upvotes: 0

Related Questions