Why isn\'t Collection.remove(Object o) generic?
Seems like Collection
could have boolean remove(E o);
Then, when you ac
Assume one has a collection of Cat
, and some object references of types Animal
, Cat
, SiameseCat
, and Dog
. Asking the collection whether it contains the object referred to by the Cat
or SiameseCat
reference seems reasonable. Asking whether it contains the object referred to by the Animal
reference may seem dodgy, but it's still perfectly reasonable. The object in question might, after all, be a Cat
, 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.
remove()
(in Map
as well as in Collection
) is not generic because you should be able to pass in any type of object to remove()
. The object removed does not have to be the same type as the object that you pass in to remove()
; it only requires that they be equal. From the specification of remove()
, remove(o)
removes the object e
such that (o==null ? e==null : o.equals(e))
is true
. Note that there is nothing requiring o
and e
to be the same type. This follows from the fact that the equals()
method takes in an Object
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 for List.equals()
says that two List objects are equal if they are both Lists and have the same contents, even if they are different implementations of List
. So coming back to the example in this question, it is possible to have a Map<ArrayList, Something>
and for me to call remove()
with a LinkedList
as argument, and it should remove the key which is a list with the same contents. This would not be possible if remove()
were generic and restricted its argument type.
In addition to the other answers, there is another reason why the method should accept an Object
, which is predicates. Consider the following sample:
class Person {
public String name;
// override equals()
}
class Employee extends Person {
public String company;
// override equals()
}
class Developer extends Employee {
public int yearsOfExperience;
// override equals()
}
class Test {
public static void main(String[] args) {
Collection<? extends Person> people = new ArrayList<Employee>();
// ...
// to remove the first employee with a specific name:
people.remove(new Person(someName1));
// to remove the first developer that matches some criteria:
people.remove(new Developer(someName2, someCompany, 10));
// to remove the first employee who is either
// a developer or an employee of someCompany:
people.remove(new Object() {
public boolean equals(Object employee) {
return employee instanceof Developer
|| ((Employee) employee).company.equals(someCompany);
}});
}
}
The point is that the object being passed to the remove
method is responsible for defining the equals
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
of Number
s and a
List
of Long
s.