Generics aren't as safe as they should be

Last night while I was working on some coding on the L here in Chicago, I realized something very interesting about the Java 5.0 generic Map interface. It seems that certain methods on this class have not been updated to support generics. Specifically the get method has this signature:

public V get(Object key);

This seems to me to be a glaring bug in the JDK interfaces. Well, I looked over on the bug list at Sun and found this bug:

Sun’s explanation for this method signature is as follows:
> – “public V get(Object key)”
> – “public boolean containsKey(Object key)”
> – “public boolean containsValue(Object value)”

> These signatures are as intended. Given a HashMap<Integer ,String>, for example, one could reasonably want to invoke get(n) where n is a Number. It may be the case that n is in fact an Integer, in which case the operation will proceed normally; null may legitimately be returned otherwise.

I have to completely disagree on this explanation. What Sun has done is allow Maps to be semi safe. Here’s why:

public class Key { ... }
public class Value { ... }

Map map = new HashMap();
map.put(myKey, myValue);

Value v = map.get("foo");

This compiles and runs and fails everytime. There is no compiler check that says, “hey, dude, you this map takes Key’s not String’s.” This means that nasty bugs can be introduced into code that should be safe using generics. The problem is that this code won’t throw an exception to tell the developer that their code is incorrect. It just returns null. So, the developer has to walk through the code line by line and double check that every call to the get method is correct.

In addition, Sun’s little rant about wanting to pass in super classes seems incorrect. If you want a Map that takes super-classes you should make it like this:

Map m;

If you want to ensure type safety then you must force the developer to cast when calling the get method and possibly have a ClassCastException thrown. Therefore, code that implements their explanation above would look like this:

Map m;
Number n = new Double(2.0);
String str = m.get((Integer) n); // ClassCastException!

I’d like to hear other peoples thoughts on this topic. Perhaps an API change can be made in 6.0.

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s