Why isn't Collection.remove(Object o) generic?
Seems like Collection<E>
could have boolean remove(E o);
Then, when you accidentally try to remove (for example) Set<String>
instead of each individual String from a Collection<String>
, it would be a compile time error instead of a debugging problem later.
remove()
(inMap
as well as inCollection
) is not generic because you should be able to pass in any type of object toremove()
. The object removed does not have to be the same type as the object that you pass in toremove()
; it only requires that they be equal. From the specification ofremove()
,remove(o)
removes the objecte
such that(o==null ? e==null : o.equals(e))
istrue
. Note that there is nothing requiringo
ande
to be the same type. This follows from the fact that theequals()
method takes in anObject
as parameter, not just the same type as the object.Although, it may be commonly true that many classes have
equals()
defined so that its objects can only be equal to objects of its own class, that is certainly not always the case. For example, the specification forList.equals()
says that two List objects are equal if they are both Lists and have the same contents, even if they are different implementations ofList
. So coming back to the example in this question, it is possible to have aMap<ArrayList, Something>
and for me to callremove()
with aLinkedList
as argument, and it should remove the key which is a list with the same contents. This would not be possible ifremove()
were generic and restricted its argument type.I always figured this was because remove() has no reason to care what type of object you give it. It's easy enough, regardless, to check if that object is one of the ones the Collection contains, since it can call equals() on anything. It's necessary to check type on add() to ensure that it only contains objects of that type.
Assume one has a collection of
Cat
, and some object references of typesAnimal
,Cat
,SiameseCat
, andDog
. Asking the collection whether it contains the object referred to by theCat
orSiameseCat
reference seems reasonable. Asking whether it contains the object referred to by theAnimal
reference may seem dodgy, but it's still perfectly reasonable. The object in question might, after all, be aCat
, and might appear in the collection.Further, even if the object happens to be something other than a
Cat
, there's no problem saying whether it appears in the collection--simply answer "no, it doesn't". A "lookup-style" collection of some type should be able to meaningfully accept reference of any supertype and determine whether the object exists within the collection. If the passed-in object reference is of an unrelated type, there's no way the collection could possibly contain it, so the query is in some sense not meaningful (it will always answer "no"). Nonetheless, since there isn't any way to restrict parameters to being subtypes or supertypes, it's most practical to simply accept any type and answer "no" for any objects whose type is unrelated to that of the collection.In addition to the other answers, there is another reason why the method should accept an
Object
, which is predicates. Consider the following sample:The point is that the object being passed to the
remove
method is responsible for defining theequals
method. Building predicates becomes very simple this way.Josh Bloch and Bill Pugh refer to this issue in Java Puzzlers IV: The Phantom Reference Menace, Attack of the Clone, and Revenge of The Shift.
Josh Bloch says (6:41) that they attempted to generify the get method of Map, remove method and some other, but "it simply didn't work".
There are too many reasonable programs that could not be generified if you only allow the generic type of the collection as parameter type. The example given by him is an intersection of a
List
ofNumber
s and aList
ofLong
s.Remove is not a generic method so that existing code using a non-generic collection will still compile and still have the same behavior.
See http://www.ibm.com/developerworks/java/library/j-jtp01255.html for details.
Edit: A commenter asks why the add method is generic. [...removed my explanation...] Second commenter answered the question from firebird84 much better than me.