This is indeed a problem. If someone calls remove(o)
and o
's type is not E
, it is usually a programming bug that tries to remove the wrong thing. Type checking failed to protect us from the bug.
Although a good IDE (IntelliJ) can detect such issues and warn us, API designers should have provided more precise signature to utilize compiler type checking. (IDE cheats here - it knows the meaning of Set.remove()
because it's a standard API. IDE will not provide the same help for custom APIs)
For query API like contains()
, it is OK to accept a non-E
argument and return a trivial false. So we can have both
boolean contains(Object o);
boolean contains2(E o);
For mutation API like remove()
, it's debatable whether it should accept a non-E
argument. However the debate is going to be moot, given the reality of erasure - there is really no choice but accept non-E argument and be silent about it. Still we can have two methods
boolean remove(Object o);
boolean remove2(E o);
In most cases, programmers can call contains2/remove2
for extra type safety.