A type-safe varargs method that initializes the map
I want to write a method that I can use to initialize the Map. First cut:
Map map(Object ... o) {for (int i = 0; i < o.length; i+=2){result.put(o[i], o[i+1])}}
Simple, but not type safe. Using generics, maybe something like:
<TKey, TValue> HashMap<TKey, TValue> map(TKey ... keys, TValue ... values)
but this syntax is not supported. So I end up with this:
public static <TKey, TValue, TMap extends Map<? super TKey, ? super TValue>> TMap map(TMap map, Pair<? extends TKey, ? extends TValue> ... pairs) {
for (Pair<? extends TKey, ? extends TValue> pair: pairs) {
map.put(pair.getKey(), pair.getValue());
}
return map;
}
public static <TKey, TValue> HashMap<? super TKey, ? super TValue> map(Pair<? extends TKey, ? extends TValue> ... pairs) {
return map(new HashMap<TKey, TValue>(), pairs);
}
public static <TKey, TValue> Pair<TKey, TValue> pair(TKey key, TValue value) {
return new Pair<TKey, TValue>(key, value);
}
public static final class Pair<TKey, TValue> {
private final TKey key;
private final TValue value;
Pair(TKey key, TValue value) {this.key = key; this.value = value; }
public TKey getKey() {return key;}
public TValue getValue() {return value;}
}
But when I try to do this, I need to do this:
private static final Map<? extends Class<? extends Serializable>, ? super TypeHandler<? extends Serializable > > validCodeTypes =
/* (Map<? extends Class<? extends Serializable>, ? super TypeHandler<? extends Serializable >>) */
map(
pair(Integer.class, new IntHandler()),
pair(Integer.TYPE, new IntHandler()),
pair(Character.class, new CharHandler()),
pair(Character.TYPE, new CharHandler()),
pair(String.class, new StringHandler())
);
private interface TypeHandler<TType extends Serializable> {}
private static class CharHandler implements TypeHandler<Character> {}
private static class IntHandler implements TypeHandler<Integer> {}
private static class StringHandler implements TypeHandler<String> {}
Can anyone tell me how to code my map () methods so that it is completely generic, but doesn't need to be cast?
To make life easier for yourself, never use a wildcard return type. Substitution types are generally only intended for method parameters.
So try this:
public static <TKey, TValue, TMap extends Map<TKey, TValue>> TMap map(TMap map, Pair<? extends TKey, ? extends TValue>... pairs) {
for (Pair<? extends TKey, ? extends TValue> pair: pairs) {
map.put(pair.getKey(), pair.getValue());
}
return map;
}
public static <TKey, TValue> HashMap<TKey, TValue> map(Pair<? extends TKey, ? extends TValue>... pairs) {
return map(new HashMap<TKey, TValue>(), pairs);
}
I haven't tested it, but let it know how you feel.
PS, rather than using the generated type Pair
, you might find it easier to use Map.Entry
.
Why not this? Am I missing something?
import java.util.HashMap;
import java.util.Map;
public class ToHash {
public static <K, V> Map<K, V> toHash(Object... objects) {
Map<K, V> map = new HashMap<K, V>(objects.length / 2);
if (objects.length % 2 != 0) {
throw new IllegalArgumentException("Odd number of elements: " + objects.length);
}
for (int i = 0; i < objects.length; i += 2) {
map.put((K) objects[i], (V) objects[i + 1]);
}
return map;
}
}
pgdx: Your technique certainly works, but that doesn't stop me from saying something like:
Map<Long, Date> map = toHash("hello", "world");
I was looking for a way that would allow the compiler to pick up any type of error.