如何在java Map接口中关联泛型类型参数的值?

时间:2011-11-10 19:57:27

标签: java generics types map

我不认为问题的标题会很清楚,但这个想法很简单。

假设我有一个Map类型变量。

Map<K,V> myMap;

但我想建立K和V之间的关系,例如,我想说 此Map将某些类的集合与该类的objets相关联。类似的东西:

Map<Set<T>, T> myMap;

但不适用于特定类型T.我希望此地图接受

等条目
(Set<String>, String),
(Set<Integer>, Integer)
...

是否有可能的myMap声明允许我有这种行为?如果我错误地解释自己或者我之前有过概念错误,请告诉我。

4 个答案:

答案 0 :(得分:2)

遗憾的是,Java泛型不可能实现这一点。如果Java允许更高阶类型参数,那么可以定义Map之类的内容:

public interface Map<V<>> {  // here V<> is my hypothetical syntax for a 
                             // type parameter which is itself generic...
    <K>
    V<K> put(K key, V<K> value);
    ...
}

而不是实际的java.util.Map

public interface Map<K, V> {
    V put(K key, V value);
    ...
}

您可以看到问题是K为整个班级宣布一次,而不是每次拨打.put()

足够的幻想,你能做什么?我认为最好是创建一个Map<Set<?>, Object>并将其作为私人成员包装。然后,您可以自由创建自己的put()get(),其中考虑了类型之间的预期“关系”:

class SpecialMap {
    private Map<Set<?>, Object> map = ...;

    public <T>
    T put(Set<T> key, T value) {
        return (T) map.put(key, value);
    }

    public <T>
    T get(Set<T> key) {
        return (T) map.get(key);
    }
}

答案 1 :(得分:1)

你要做的事情似乎不是一个好的死亡,因为每个Set<T>总是不等于另一个Set<T>,即使是相同的类型 - 使用集合作为键更多或少用无用了。

那就是说,你不需要定义一个新类 - 你可以要求一个方法来接受这样的地图:

public static <T> void process(Map<Set<T>, T> map) {
    for (Map.Entry<Set<T>, T> entry : map) {
        Set<T> key = entry.getKey();
        T value = entry.getValue();
        // do something
    }
}

答案 2 :(得分:0)

泛型无法让编译器为每个T调用验证不同的put()。换句话说,你不能拥有相同的地图并执行:

myMap.put(new HashSet<String>(), "foo");
myMap.put(new HashSet<Integer>(), 1);

如果您需要,则可能需要存储<Object>并使用instanceof或其他一些黑客进行验证。

现在,您肯定可以做类似的事情:

public class MyMap<T> extends HashMap<Set<T>, T> {
    ...

然后你可以这样做:

MyMap<String> myMap = new MyMap<String>();
Set<String> set = new HashSet<String>();
myMap.put(set, "foo");

请注意,密钥必须包含有效的hashCode()equals()方法,Set可能会很昂贵。

答案 3 :(得分:0)

我认为用Java泛型实现编译时检查是不可能的。但是它在运行时非常简单。恰好是一个简短的装饰者:

public class FancyTypeMapDecorator implements Map<Set<? extends Object>, Object> {

    final Map<Set<? extends Object>, Object> target;

    public FancyTypeMapDecorator(Map<Set<? extends Object>, Object> target) {
        this.target = target;
    }

    @Override
    public Object put(Set<? extends Object> key, Object value) {
        final Class<?> keyElementType = key.iterator().next().getClass();
        final Class<?> valueType = value.getClass();
        if (keyElementType != valueType) {
            throw new IllegalArgumentException(
                "Key element type " + keyElementType + " does not match " + valueType);
        }
        return target.put(key, value);
    }

    @Override
    public void putAll(Map<? extends Set<? extends Object>, ? extends Object> m) {
        for (Entry<? extends Set<? extends Object>, ? extends Object> entry : m.entrySet()) {
            put(entry.getKey(), entry.getValue());
        }
    }

    //remaining methods are simply delegating to target

}

以下是它的工作原理:

final Map<Set<? extends Object>, Object> map =
    new FancyTypeMapDecorator(new HashMap<Set<? extends Object>, Object>());

Set<? extends Object> keyA = Collections.singleton(7);
map.put(keyA, 42);

Set<? extends Object> keyB = Collections.singleton("bogus");
map.put(keyB, 43);

第二个put会抛出异常。

然而这两个实现(我甚至不认为它会因空Set作为键而失败)而且usage / API会触发警钟......你真的想要处理这样的问题吗?结构体?也许你需要重新思考你的问题?你实际试图实现什么?