6

Why boolean containsAll(Collection < ?> c); method of collection framework is allowed for every type ?.But boolean addAll(Collection< ?extends E> c); allow for ? extends E.So,i wrote a program for clarification. Here is my program

public class ContainAllTest {
    // take ServiceDto 
    ArrayList<ServiceDto> resultList = new ArrayList<ServiceDto>();

    void Test() {

        ServiceDto serviceDto = new ServiceDto();
        serviceDto.setName("test");
        resultList.add(serviceDto);
        // another arraylist that takes String 
        ArrayList<String> resultList1 = new ArrayList<String>();
        resultList1.add("test");
        // no error, goes for run time.Contain all checking is done for two generic type ServiceDto and String:
        resultList.containsAll(resultList1);
        // error shown at compile time,as addAll take ServiceDto as generic type but the generic type for resultList1 take String:
        resultList.addAll(resultList1);    
    }

So,my question is when can i get advantage of resultList.containsAll(resultList1); when the generic type is different.In my case String and ServiceDto.Was there some thing wrong replacing boolean containsAll(Collection< ? > c) with boolean containsAll(Collection< ?extends E> c)

Volker Stolz
  • 7,274
  • 1
  • 32
  • 50
abishkar bhattarai
  • 7,371
  • 8
  • 49
  • 66
  • Equals and hashCode are methods in Object, that is >. When you search for a collection containing something you don't want to need to cast it to the appropriate type each time. – Igor Rodriguez Aug 15 '13 at 07:14
  • @artbristol: Yes, this is essentially the same question, and that has a better answer. I will vote to mark it as a duplicate. – Tom Anderson Aug 15 '13 at 07:55
  • It would be tedious if I had to copy the contents of my Collection> to a Collection extends E> just so I could call containsAll(). It's essentially equivalent to the equals() method accepting a parameter of type Object. (Ok, with type erasure both Collections are just Collection and I could cast it, but that is just a hack and we should write our code as if it wouldn't work.) – Adrian Pronk Aug 15 '13 at 10:38

3 Answers3

3

I'm guessing the reason is that containsAll (and contains, remove, removeAll) uses Object#equals for the comparison.

You could, conceivably, have an overriden Object#equals method in E that can return true for objects of some unrelated class. Not that it would be a good idea, but it could be a valid implementation.

Tobias Brandt
  • 3,393
  • 18
  • 28
  • 1
    It's sometimes a good idea. For example, `java.util.List` requires that `.equals()` return true for lists with the same content, even for different list classes. – newacct Aug 16 '13 at 18:56
2

This isn't to give an advantage it's to save cpu ticks. Generics are erased by the compiler and replaced with casts.

For the addAll method type safety needs to be taken into account. The user should only be allowed to add a Collection<E> or some subclass of E to a Collection<E>.

If you look at the source code for AbstractCollection you see this method:

public boolean addAll(Collection<? extends E> c) {
    boolean modified = false;
    for (E e : c)
        if (add(e))
            modified = true;
    return modified;
}

When compiled it will look (something) like

public boolean addAll(Collection c) {
    boolean modified = false;
    for (Object e : c)
        if (add((E)e))
            modified = true;
    return modified;
}

I.e. each and every element of the collection to be added needs to be cast from Object to E before being added.

For the containsAll method it doesn't matter. As the equals method is defined as equals(Object other) you can safely call it with any other Collection and there is no risk of ClassCastExceptions. By avoiding the use of generics the compiler can avoid adding in casts.

Boris the Spider
  • 59,842
  • 6
  • 106
  • 166
  • 7
    I don't think it has anything to do with "cpu ticks". – assylias Aug 15 '13 at 07:39
  • @assylias Casting ain't free. But it does also have the advantage of providing a better interface specification. – Boris the Spider Aug 15 '13 at 07:42
  • 4
    Casting is maybe not free but I don't think it has anything to do with the design of the class. – assylias Aug 15 '13 at 07:48
  • 1
    It's a single instruction (`checkcast`). I don't think it matters. – Tobias Brandt Aug 15 '13 at 07:52
  • 1
    More importantly: a cast to ˋEˋ is an unchecked cast that only checks against ˋObjectˋ at runtime. So it's a no-op and **it is free** in this case! So that's certainly not the reason behind the design decision. I suspect the reason has to do with backwards compatibility. – Joachim Sauer Aug 15 '13 at 07:56
  • -1 This is just wrong. There is no cast when adding the element to the collection, in none of the cases. – Philipp Wendler Aug 15 '13 at 11:27
1

The reason is the same as for the methods add and contains.

add takes an argument of the generic type of the collection, because only such objects may be added to the collection. That's the whole point of using generics in collections.

contains (as well as other methods in the collection framework like remove and Map.get) accepts any object as parameter. There are at least two reasons.

First, as Tobias Brandt said, there might be objects of a completely separate type that are "equal" (as defined by their equals implementation) to an object in the collection.

Second, every collection Collection<E> can be seen as Collection<? extends D> where D is a super class of E, or even as Collection<?> (which is the same as Collection<? extends Object>). If you do this upcast, you can't call the add method anymore, because it's signature would look like add(?) and the compiler forbids calling it because it could never ensure type safety (and it's good that you can't call add because you could add wrong types to the collection). However, it might still be useful to call contains and this is always type safe, so why shouldn't it be allowed? In order to allow this, the contains method needs to have Object as parameter type, otherwise it could not be called similar to add.

The signatures of addAll and containsAll just follow this same principle.

Philipp Wendler
  • 11,184
  • 7
  • 52
  • 87
  • 1
    "However, it might still be useful to call contains and this is always type safe" But this is again because `Object.equals()` takes all types. If `Object.equals()` didn't take all types, then it would not always be type safe. – newacct Aug 16 '13 at 18:58