In this answer, I tried to explain why the Collection method add
has the signature add(E)
while remove
is remove(Object)
. I came up with the idea that the correct signature should be
public boolean remove(? super E element)
And since this is invalid syntax in Java, they had to stick to Object
, which just happens to be super E
(supertype of E
) for any E
. The following code explains why this makes sense:
List<String> strings = new ArrayList();
strings.add("abc");
Object o = "abc"; // runtime type is String
strings.remove(o);
Since the runtime type is String, this succeeds. If the signature were remove(E)
, this would cause an error at compile-time but not at runtime, which makes no sense. However, the following should raise an error at compile time, because the operation is bound to fail because of its types, which are known at compile-time:
strings.remove(1);
The remove
takes an Integer
as an argument, which is not super String
, which means it could never actually remove anything from the collection.
If the remove
method was defined with the parameter type ? super E
, situations like the above could be detected by the compiler.
Question:
Am I correct with my theory that remove
should have a contravariant ? super E
parameter instead of Object
, so that type mismatches as shown in the above example can be filtered out by the compiler? And is it correct that the creators of the Java Collections Framework chose to use Object
instead of ? super E
because ? super E
would cause a syntax error, and instead of complicating the generic system they simply agreed to use Object
instead of super
?
Also, should the signature for removeAll
be
public boolean removeAll(Collection<? super E> collection)
Note that I do not want to know why the signature is not remove(E)
, which is asked and explained in this question. I want to know if remove
should be contravariant (remove(? super E)
), while remove(E)
represents covariance.
One example where this does not work would be the following:
List<Number> nums = new ArrayList();
nums.add(1);
nums.remove(1); // fails here - Integer is not super Number
Rethinking my signature, it should actually allow sub- and supertypes of E
.
This is a faulty assumption:
It's the same reasoning that
.equals
accepts an object: objects don't necessarily need to have the same class in order to be equal. Consider this example with different subtypes of List, as pointed out in the question @Joe linked:This would not be possible with the signature you proposed.