[英]how can I relate values of generic type parameters in java Map interface?
I don't think the title of the question will be clear, but the idea is simple. 我不认为问题的标题会很清楚,但这个想法很简单。
Suppose I have a Map type variable. 假设我有一个Map类型变量。
Map<K,V> myMap;
but I want to establish a relation between K and V. for example, I'd like to say that this Map relates Sets of some class to objets of that class. 但是我想建立一个K和V之间的关系,例如,我想说这个Map将某些类的集合与该类的objets相关联。 Something like:
就像是:
Map<Set<T>, T> myMap;
but not for a specific type T. I'd like this Map to accept entries like 但不是特定类型T.我希望这个地图接受像这样的条目
(Set<String>, String),
(Set<Integer>, Integer)
...
Is there a possible declaration for myMap that allows me to have this behavior? 是否有可能的myMap声明允许我有这种行为? Please let me know if I'm explaining myself wrongly or if I have a previous conceptual error.
如果我错误地解释自己或者我之前有过概念错误,请告诉我。
Sadly this is not possible with Java generics. 遗憾的是,Java泛型不可能实现这一点。 If Java allowed higher order type parameters, then one could have defined
Map
something like: 如果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);
...
}
instead of the actual java.util.Map
: 而不是实际的
java.util.Map
:
public interface Map<K, V> {
V put(K key, V value);
...
}
You can see that the problem is that K
is declared once for the whole class and not for each call to .put()
. 您可以看到问题是
K
为整个类声明了一次,而不是每次调用.put()
。
Enough fantasizing, so what can you do? 足够的幻想,你能做什么? I think the best is to create a
Map<Set<?>, Object>
and wrap it as a private member. 我认为最好是创建一个
Map<Set<?>, Object>
并将其包装为私有成员。 Then you are free to create your own put()
and get()
which take into account the intended "relation" between types: 然后你可以自由创建自己的
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);
}
}
What you are trying to do doesn't seem like a good diea, because each Set<T>
is always not equal to another Set<T>
even if of the same type - using Sets as keys is more or less useless. 你想要做的事情似乎不是一个好的迪亚,因为每个
Set<T>
总是不等于另一个Set<T>
即使是相同的类型 - 使用Sets作为键或多或少是无用的。
That said, you don't need to define a new class - you can require a method to accept such a map: 也就是说,您不需要定义新类 - 您可以要求一个方法来接受这样的地图:
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
}
}
There is no way with generics to have the compiler verify a different T
for each put()
call. 对于每个
put()
调用,编译器无法使用泛型验证不同的T
In other words, you can't have the same map and do: 换句话说,你不能拥有相同的地图并做:
myMap.put(new HashSet<String>(), "foo");
myMap.put(new HashSet<Integer>(), 1);
If you need this then you may have to store <Object>
and do the verification yourself using instanceof
or some other hack. 如果你需要这个,那么你可能必须存储
<Object>
并使用instanceof
或其他一些hack自己进行验证。
Now, you can definitely do something like: 现在,你绝对可以这样做:
public class MyMap<T> extends HashMap<Set<T>, T> {
...
Then you can do: 然后你可以这样做:
MyMap<String> myMap = new MyMap<String>();
Set<String> set = new HashSet<String>();
myMap.put(set, "foo");
Remember that the key has to have a valid hashCode()
and equals()
methods which might be expensive with a Set
. 请记住,密钥必须具有有效的
hashCode()
和equals()
方法,这些方法对于Set
可能很昂贵。
I don't think it's possible to achieve compile-time checking with Java generics. 我认为用Java泛型实现编译时检查是不可能的。 However it's quite simple at runtime.
但是它在运行时非常简单。 Just right a short decorator:
恰好是一个简短的装饰者:
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
}
Here's how it works: 以下是它的工作原理:
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);
Second put
throws an exception. 第二
put
抛出异常。
However both the implementation (and I don't even mean that it will fail for empty Set
as a key) and usage/API triggers an alarm bell... Do you really want to deal with such a structure? 然而这两个实现(我甚至不认为它会因为空
Set
作为键而失败)和usage / API触发警钟......你真的想要处理这样的结构吗? Maybe you need to rethink your problem? 也许你需要重新思考你的问题? What are you actually trying to achieve?
你究竟想要实现什么目标?
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.