10

I was recently bitten by a bug in which I had a Map with key type Long, but I attempted to use it with keys of type String. I essentially had something like:

Map<Long, Object> map;
...
String wrongType;
if (map.containsKey(wrongType)) {
    // Do something
} else {
    // Do something different
}

Because all of the keys in the map were of type Long, the code always executed the else block.

Since the containsKey and get methods take an argument of type Object, an object of any old type is accepted without complaint.

My confusion stemmed from the fact that the same entity is represented in two different ways in our system (sometimes as a Long, sometimes as a String); I can't easily change this. Is there any way I can catch an error like this while developing rather than during testing? Perhaps a compiler flag or some Eclipse option that is a little smarter about what sort of object I should be using with the containsKey and get methods (and their analogs in Set, too...)

dbyrne
  • 59,111
  • 13
  • 86
  • 103
reo katoa
  • 5,751
  • 1
  • 18
  • 30
  • Sonar does warn about that if I remember correctly. It is good to have a Hudson CI and Sonar set up for a Java project, it makes life a lot easier... – ppeterka Sep 12 '13 at 14:41
  • possible duplicate of [What are the reasons why Map.get(Object key) is not (fully) generic](http://stackoverflow.com/questions/857420/what-are-the-reasons-why-map-getobject-key-is-not-fully-generic) – artbristol Sep 12 '13 at 15:01
  • use IntelliJ which will warn you about it. – ZhongYu Sep 12 '13 at 15:37

3 Answers3

7

FindBugs has a test for this: GC_UNRELATED_TYPES

Running FindBugs on your code should reveal this, and a lot of other things too ;-)

rolfl
  • 17,539
  • 7
  • 42
  • 76
4

You can write a generic utility method that will provide type safety:

public static <T> boolean safeContainsKey(Map<T, ?> map, T key) {
  return map.containsKey(key);
}

public static <T, U> U safeGet(Map<T, U> map, T key) {
  return map.get(key);
}

Now you will get compile time errors if you pass in the wrong type:

//These compile fine
boolean result1 = safeContainsKey(map, 12345l);
Object obj1 = safeGet(map, 12345l);

//These cause compilation errors
boolean result2 = safeContainsKey(map, "12345");
Object obj2 = safeGet(map, "12345");

You could implement your own type safe version of the Map interface as well, but thats probably overkill.

Personally, I just run Google's CodePro Analytix which will provide useful type safety warnings.

dbyrne
  • 59,111
  • 13
  • 86
  • 103
3

The reason that the Map methods get() and contains() take Object (and not the type of the key) is they pre-date generics and to be backwardly compatible, the signatures had to stay that way.

Unfortunately, there is no compiler protection/warning against calling these methods with the wrong type.

Bohemian
  • 412,405
  • 93
  • 575
  • 722
  • +1: Doesn't solve my problem, but thanks for the information. – reo katoa Sep 12 '13 at 18:18
  • FWIW, sometime between when this question was first posted and now (2019), additional analysis was added. This kind of code will elicit an "unlikely argument type" warning from several compilers and IDEs these days, _if_ the key type is known at compilation. – Ti Strga Feb 25 '19 at 21:24