Question

I'm wanting to write a method that I can use to initialise a 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 that syntax isn't supported. So eventually I come to 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 it out, I need to cast it:

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 entirely general yet doesn't need to be casted?

Was it helpful?

Solution

To make life easier for yourself, never use a return type that contains wildcards. Wildcard types, in general, are for method parameters only.

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 give it a go and see how you fare.

P.S., rather than using a made-up Pair type, you may find it easier to use Map.Entry.

OTHER TIPS

Why not this? Did I misunderstand 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 it does not prevent me from saying something like:

Map<Long, Date> map = toHash("hello", "world");

I was looking for a way which would allow the compiler to pick up on any type-mismatch errors.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top