Michael Bobick
Michael Bobick

Reputation: 8997

Why does Java Map<K, V> take an untyped parameter for the get and remove methods?

I ran into a bug in my code where I was using the wrong key to fetch something from a Java map that I believed was strongly typed using Java generics. When looking at the Map Javadocs, many of the methods, including get and remove, take an Object as the parameter instead of type K (for a Map defined as Map). Why is this? Is there a good reason or is it an API design flaw?

Upvotes: 13

Views: 3709

Answers (3)

Luke Hutteman
Luke Hutteman

Reputation: 1931

This was done so that if the type parameter is a wildcard, these methods can still be called.

If you have a Map<?, ?>, Java won't allow you to call any methods that are declared with the generic types as arguments. This prevents you from violating the type constraints so you cannot, for instance, call put(key, value) with the wrong types.

If get() were defined as get(K key) instead of the current get(Object key), it too would have been excluded due to this same rule. This would make a wildcarded Map practically unusable.

In theory, the same applies to remove(), as removing an object can never violate the type constraints either.

Here is an example of code that would not have been possible if get had been declared as get(T key):

public static <K,V> Map<K, V> intersect(Map<? extends K, ? extends V> m1, Map<? extends K, ? extends V> m2) {
    Map<K,V> result = new HashMap<K, V>();
    for (Map.Entry<? extends K, ? extends V> e1 : m1.entrySet()) {
        V value = m2.get(e1.getKey()); // this would not work in case of Map.get(K key)
        if (e1.getValue().equals(value)) {
            result.put(e1.getKey(), e1.getValue());
        }
    }
    return result;
}

e1.getKey() returns an object of some unknown subtype of K (the subtype used by m1), but m2 uses a potentially different subtype of K. Had Map.get() been declared as get(K key), this usage would not have been allowed.

Upvotes: 3

jarnbjo
jarnbjo

Reputation: 34313

Because the map will return a value if the object passed to the get method is equal to any key stored in the map. Equal does not mean that they have to be of the same type, but that the key's and the passed object's equal methods are implemented in such a way, that the different object types are mutually recognized as equal.

The same applies of course to the remove method.


Example of valid code, which would break (not compile) if the get method only allowed parameters of type K:

LinkedList<Number> k1 = new LinkedList<Number>();
k1.add(10);

ArrayList<Integer> k2 = new ArrayList<Integer>();
k2.add(10);

Map<LinkedList<Number>, String> map = new HashMap<LinkedList<Number>, String>();

map.put(k1, "foo");
System.out.println(map.get(k2));

Upvotes: 5

Jim
Jim

Reputation: 22656

I think this is for backwards compatibility with older versions of the Map interface. It's unfortunate that this is the case however as you're right, it would be much better if this took the correct type.

Upvotes: 5

Related Questions