4

I have two maps in my class (I am new to generics)

private Map<Integer, Integer> aMap = new ConcurrentHashMap<Integer, Integer>();
private Map<Integer, Short> bMap = new HashMap<Integer, Short>();

If key does not exist in map I want to get a zero value. So I have made this wrapper method to minimize typing containsKey(key)

@SuppressWarnings("unchecked")
private <T extends Number> T getValue (Map<Integer, T> map, Integer key) {
    return (T) ((map.containsKey(key)) ? map.get(key) : 0);
}

I call it like

Integer a = getValue(aMap, 15); //okay in any case
Short b = getValue(bMap, 15); //15 key does not exist

For second case it gives me:

ClassCastException: java.lang.Integer cannot be cast to java.lang.Short

So probably I would need to do something like : new Number(0), but Number is abstract.

How can I fix it?

EDIT:

My idea is to do arithmetic operations without additional ifs:

Integer a = getValue(aMap, 15);
a = a + 10;
Nikolay Kuznetsov
  • 9,467
  • 12
  • 55
  • 101
  • 4
    If you return null instead of zero, you won't have this problem. And its probably more correct anyway since zero is actually a valid value (unless you are saying your map will ***never*** contain those). – Perception Mar 04 '13 at 10:28

4 Answers4

6

One way is to supply the default value as an argument to your function:

private <T extends Number> T getValue (Map<Integer, T> map, Integer key, T dflt) {
    return (T) ((map.containsKey(key)) ? map.get(key) : dflt);
}

public static void main(String[] args) {
    Integer a = getValue(aMap, 15, 0); //okay in any case
    Short b = getValue(bMap, 15, (short)0); //15 key does not exist
}
NPE
  • 486,780
  • 108
  • 951
  • 1,012
3

Well, you can't do much about that without also providing T in a way that code can look at.

The simplest approach at that point would probably be to keep a map of 0 values:

private static Map<Class<?>, Number> ZERO_VALUES = createZeroValues();

private static Map<Class<?>, Number> createZeroValues() {
    Map<Class<?>, Number> ret = new HashMap<Class<?>, Number>();
    ret.put(Integer.class, (int) 0);
    ret.put(Short.class, (short) 0);
    ret.put(Long.class, (long) 0);
    // etc
}

Then:

private <T extends Number> T getValue (Map<Integer, T> map, Integer key, Class<T> clazz) {
    return clazz.cast(map.containsKey(key) ? map.get(key) : ZERO_VALUES.get(clazz));
}

Then you'd unfortunately have to call it as:

Short b = getValue(bMap, 15, Short.class);

Basically this is a limitation of Java generics :(

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
2

In situations like this, I just override the get() method:

 private Map<Integer, Short> bMap = new HashMap<Integer, Short>() {
     @Override
     public Short get(Object key) {
         return containsKey(key) ? super.get(key) : new Short(0);
     }
 };

Then you can just use it anywhere and it will behave as you specified.

Nikolay Kuznetsov
  • 9,467
  • 12
  • 55
  • 101
Bohemian
  • 412,405
  • 93
  • 575
  • 722
  • Thx :) I use this technique often. btw, you can "unaccept" answers, and accept others :) – Bohemian Mar 04 '13 at 11:29
  • Btw, why is it `super.get(key)`? Should not it be just `get.key();`? – Nikolay Kuznetsov Mar 05 '13 at 04:44
  • Calling `get(key)` would be recursive - the method would call itself (because you are *overriding* the get method). Calling `super.get(key)` calls the implementation for `HashMap`. Remember that an anonymous class is a *subclass* of the coded class. – Bohemian Mar 05 '13 at 05:33
  • "Remember that an anonymous class is a subclass of the coded class." I missed that, thanks! – Nikolay Kuznetsov Mar 05 '13 at 05:40
0

Cast 0 to short explicitly as it will be int by default. And then convert to Short wrapper. In java u cannot directly convert from primitive to a Wrapper of a different type.

  • If you are thinking about writing `(short) 0` in the `getValue` method, this will work for shorts and ints, but not e.g. for doubles. – tobias_k Mar 04 '13 at 10:52
  • Then as said in an earlier post you will have to have different values in map. Or else return null. Because implicit conversion wontb possible – user1441664 Mar 04 '13 at 10:58
  • Given what the OP asked, it sounds like it doesn't need to work for doubles? – David Conrad Mar 04 '13 at 19:18