Generic Usage in Collections Framework
I'm confused about these methods:
public boolean remove(Object object)
public boolean removeAll(Collection<?> collection)
public boolean contains(Object object)
public boolean containsAll(Collection<?> collection)
public boolean retainAll(Collection<?> c)
Shouldn't the signatures for these methods be:
public boolean remove(E object)
public boolean removeAll(Collection<E> collection)
public boolean contains(E object)
public boolean containsAll(Collection<E> collection)
public boolean retainAll(Collection<E> c)
My reasoning is that if the collection contains elements of type E, why would you want to remove (all), retain, or see if it contains anything other than elements of type E? And if generics weren't used, then E would just be object (or ?) anyway, and the behavior would be just as they are now.
I just think this would clean up the interface a lot and make it easier for custom Collection implementations. In my case, a Binary Min Heap and Ternary Min Heap.
# 1
Whatever the reason was, it was clearly the same reason for all those methods. Instead of speculating on it myself, here's a blog entry that does the same speculating: http://www.dynamicobjects.com/d2r/archives/003108.html
# 2
Thanks for the reply. :)If anyone else has any information to share, I would appreciate it.
# 3
The interfaces don't allow you to put in anything that isn't of type E (although that's laughably trivial to defeat) so logically, you can't remove anything other than E's anyway. Still a bit untidy, though, I agree.
# 4
It's to do with backwards compatibility.See http://www.ibm.com/developerworks/java/library/j-jtp01255.html#3.0 for some explanation.
# 5
I read that site, but I'm still confused.
public interface Collections<E extends Comparable><E>>
{
public boolean remove(E object);
public boolean removeAll(Collection<E> collection);
public boolean contains(E object);
public boolean containsAll(Collection<E> collection);
public boolean retainAll(Collection<E> c);
}
If you use generics, you would specify the type of E. It would be something like this:
ArrayList<String> list = new ArrayList<String>();
In each of those methods, you would want to remove a String or retain a collection of Strings, for example. But, if you did not use generics (either by choice or because of backwards compatibility):
ArrayList list = new ArrayList();
In this case, E would be treated as Object. When that happens, the generic methods would mimic the current methods that have Object as a parameter.
By changing the interface, I don't see how backwards compatibility would be broken. In older code, before generics, E would be treated as Object just as it is in 1.5 and 6 when you don't use generics. However, if you did use generics, the method signatures would reflect the type stored in the collection.
# 6
The site actually gives an example. Taken your definition for Collection, the following code would no longer be valid:Collection<Integer> ints = ...;
Collection<Object> objs = ...;
objs.removeAll(ints);
because a Collection of Integers is no subtype of a Collection of Objects.
€dit: The above is for your interface definition. But even defining removeAll to take Collection<? extends E> won't work, as calling it the other way around was previously to Java5 valid code:ints.removeAll(objs);
Message was edited by: stefan.schulz
# 7
I think I can answer this.
If you use your proposed signatures, you are restricting the types that you can give as an input.
Whats wrong with calling contains() on an object that isn't the same as the type of the collection (possibly a super class of the collections element type)?
Basically, because <?> and Object are used, you are free to ask whether e.g a List containing String classes might "contain" an Integer class, for example (a String element is equal to the Integer input).
The String class might have an unusual equals method (thats usually the method that is used when trying to find out whether the input is contained by the collection) that may even determine that some String elements equal the Interger input.
Like:
("45" (as the string)) equals (45 (the int value, not the string))
Get it?
Essentially, it provides flexibility.
(And backwards compatibility as well).
Hope that answers your questions.
# 8
@diku: implementing equals like you suggested would be bad style and actually breaking the equals contract. So be sure that this is not the reason to use Object as parameter types. And it actually would fail with any hash-based Set or Map implementation, as the hashCodes are different,
# 9
You're right.I used an extreme example in an attempt to demonstrate what using <?> and Object would allow you to do.It wasn't meant as an encouragement to implement equals in such a creative way.
# 10
> @diku: implementing equals like you suggested would> be bad style and actually breaking the equals> contract. I think I'd frown on that usage, but I'm not sure it actually breaks the contract - can you cite chapter and verse if you're sure I'm wrong
# 11
It definitely breaks a.equals(b) <=> b.equals(a).
# 12
Er, how so?I assume we're not actually talking about String and Integer, but rather analagous types (if it were actually String and Integer, well, they're not actually implemented like that, so the question doesn't arise).
# 13
I also replaced String with custom class in mind, so, the "String" class has that unusal equals to match Integers as well. Hence, when testing an Integer to be equal to a "String" it would always fail.
I did not see Integer to have the equivalent equals being implemented from what diku posted, though.