I'm really not clear as to what your question is, but if you're asking that why is it that we have get(Object key) and not get(K key), is because the key in the map is an Object type which could be anything, for example String, Integer etc. whereas K is no type. Hence the method in the interface defines the type of key as Object.
Generics are used to make life for the client of a generic class easier. It's main application is that return values don't have to be cast back to the actual type by the client, but that this behavior is automatically inserted by the compiler.
A second application is that type checks are automatically performed by the compiler as well. A List<String> contains only Strings, so the List needs to check that arguments of the add() method are indeed of type String. Generics makes sure that this is handled automatically.
So if generics are designed to make things easier, why would we restrict a client from using the Object type for methods get() and remove()? It's not like passing an Integer to a List<String>'s remove() method would break any invariants. The list doesn't contain Integers, so nothing will change after calling remove(). This is in contrast to the add() method. If it was allowed to add an Integer to a List<String>, we would break class invariants.
Leon also gave an example of how it can be useful. If you don't know what the type argument of a generic instance is, it would be impossible to call methods that require that generic type argument. Using the Object type instead allows such methods to be called.
The mind is a strange and wonderful thing. I'm not sure that it will ever be able to figure itself out, everything else, maybe. From the atom to the universe, everything, except itself.
John, what about the put(K, V) method? I'm sure you don't mean to imply backwards compatibility is broken because it uses generic parameter types. And what about List's add(E) method? In fact, what about every other pre-generics class that has been retrofitted to use generics?
No, Object is a good parameter type because it makes sense, not because of backwards compatibility.
@Stephan: If you read the contract for get method, it says:
More formally, if this map contains a mapping from a key k to a value v such that (key==null ? k==null : key.equals(k)), then this method returns v; otherwise it returns null. (There can be at most one such mapping.)
So it is not required for the parameter to be the same Type as the generic K, but only to be an object such as o.equals(someKeyInsideTheMap) returns true. In Java, two objects can be equal without having to be the same type.
If Mr. Josh Bloch (the JCF designer) had changed that, and the method signature were get(K key), then applications written in old pre-generics Java would not work the same. Hence the compatibility issue.
@Stephan: When I talk about compatibility, I'm not talking about bytecode compatibility, but to maintain the new contract compatible and consistent with the old one.
In the old contract, there was no need for an object to be in the key set of a Map if we passed it as parameter in the get(Object o) method. The only requirement was that if o.equals(someKey) returned true, then an object from the map is retrieved. It was decided not to change the old contract, being the main reason (in my opinion) to maintain compatibility (with the way things worked in Java 1.4).
Compatibility (in a broad sense of the word) was a big factor -if not the main one - in the generics implementation. Personally, I'd rather use get(K) or get(K extends Object), which I consider a better and more consistent design. This is also the approach taken in C#.
You can read more about this (and in a better English) in a 2007 post written by Kevin Bourrillion.
John Schubert wrote: The only requirement was that if o.equals(someKey) returned true, then an object from the map is retrieved. It was decided not to change the old contract, being the main reason (in my opinion) to maintain compatibility (with the way things worked in Java 1.4).
Why would they have to change the contract? The contract is the same, regardless of the type of the parameter being used. The contract said nothing about what type the parameter must have. The only thing that said what type the parameter must have is the parameter itself, and the type of the parameter can be changed without breaking backwards compatibility, because of type erasure. Even the article you linked to says the backwards compatibility argument is irrelevant.
It's simply useful to have Object as the argument type, since it allows *new code* to retrieve and remove values from a map even if the code doesn't know the exact type of the keys, which is the case when it's handling a map with wildcard type arguments.
Joshua Bloch says that their first attempts at making these methods generic were awkward, and there are many reasonable programs that wouldn't work if they had.
Stephan van Hulst wrote:
and the type of the parameter can be changed without breaking backwards compatibility, because of type erasure
Stephan van Hulst wrote:Joshua Bloch says that their first attempts at making these methods generic were awkward, and there are many reasonable programs that wouldn't work if they had.
Ok, so you say one thing and the contrary. Well, anyway, I have no intention to make of this post an endless discussion about compatibility. As I've said, It is just my opinion. And I think that Map<K,V> with full generics on every method is the most consistent design. Mr. Hejlsberg was also of this opinion, so the Dictionary collection in C# is fully generic, and nobody is complaining of it.
Making the parameters generic (with the introduction of generics) would not break old code. Java 1.5+ code however would have difficulties. I tend to think that the Collections API would use the Object type for many methods even if Java had been designed with generics from the start.