Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java Generics erasure in Multimaps

I have two Multimaps of Strings indexed by (i) Integers and (ii) Doubles and a routine to output lists of the Strings.

public static void outputInteger(Multimap<Integer, String> map) {
    for (Integer key : map.keySet()) {
        Collection<String> strings = map.get(key);
        output(strings);
    }
}

public static void outputDouble(Multimap<Double, String> map) {
    for (Double key : map.keySet()) {
        Collection<String> strings = map.get(key);
        output(strings);
    }
}

I would like to combine these into a single routine using Number as the superclass of Integer and Double

public static void outputNumber(Multimap<? extends Number, String> map) {
    for (Number key : map.keySet()) {
        Collection<String> ids = map.get(key); //** 
    }
}

but the asterisked line does not compile

The method get(capture#5-of ? extends Number) in the type 
Multimap<capture#5-of ? extends Number,String> is not 
applicable for the arguments (Number)

How do I tackle this?

like image 557
peter.murray.rust Avatar asked Nov 28 '22 16:11

peter.murray.rust


1 Answers

The declaration

Multimap<? extends Number, String> map;

indicates that map has a key type that is an unknown but specific subtype (inclusive) of Number. In other words, the compiler thinks it could be a Multimap<Integer, String>, or Multimap<Short, String>, or Multimap<Number, String>, etc. For this reason you can't call map.get(Number), because as far as the compiler knows, it could be a Multimap<Double, String>.

The reason this can't be done is more evident with put. Should you be able to do put(Number, String) on map? No, because if it happened to be a Multimap<Integer, String>, you could then add a Double key which will then have violated the integrity of the map.

With the normal Map<K, V> interface, this isn't a problem as get is defined as get(Object), not get(K).

Paul's answer has a great workaround to this situation. Essentially he is using an intermediate generic method to give the unknown type (in this case represented by capture#5-of ? extends Number) a name (the type parameter T). This allows you to associate the captures occurring in two different contexts so that you can do something across those contexts.

like image 67
Mark Peters Avatar answered Dec 05 '22 11:12

Mark Peters