假设您想使用 Map
并且您有以下要求:您希望每个键映射到相同类型的值。
Map<???> map = ...;
map.put(42, 15);
map.put("hello world", 15); // compile time error, because you cannot map from string to int.
map.put("hello world", "foobar");
map.put(new Foo(), new Foo());
Integer i = map.get(42);
String s = map.get("hello world");
Foo f = map.get(new Foo());
当然,上面的代码不会编译,但诀窍是你从 T
映射。至T
哪里T
未在实例化时定义。映射仅返回与参数相同的类型。当然它可以变得更有趣,比如从T
映射至List<T>
。这没有丑陋的选角吗?
Scala 的类型系统通常被认为比较先进,Scala 有解决方案吗?
如果上述两个问题的答案都是“否”,那么是否有任何语言支持这种类型?
注意: 上面代码中的 Map 只是采用两个泛型参数的类型的示例。我对 Map 并不特别感兴趣,但对类型系统更感兴趣。
最佳答案
这并不完全是您所描述的,但 Java 中的一个众所周知的模式是类型安全异构容器 ( Effective Java Item 29 ),它映射 Class<T>
到 T
的实例。 Guava提供 ClassToInstanceMap
实现此模式的接口(interface)。
在 Java 中,您可以临时调整您所描述的类型,但它不会很漂亮。
public class TTMap extends ForwardingMap<Object, Object> {
private final HashMap<Object, Object> delegate = new HashMap<>();
@Override
protected Map<Object, Object> delegate() {
return delegate();
}
@SuppressWarnings("unchecked")
public <T> T getType(T key) {
return (T)delegate().get(key);
}
@SuppressWarnings("unchecked")
public <T> T putType(T key, T value) {
return (T)delegate().put(key, value);
}
@Override @Deprecated
public Object put(Object key, Object value) {
Preconditions.checkState(key == value || value.getClass().equals(key.getClass()));
return delegate.put(key, value);
}
@Override @Deprecated
public void putAll(Map<? extends Object, ? extends Object> map) {
standardPutAll(map);
}
}
这使用 ForwardingMap
给你一个Map<Object, Object>
但由于运行时约束,键和值将始终具有相同的类型。您需要使用getType()
和putType()
避免运行时错误的风险; put()
和putAll()
只能在运行时异常的情况下强制执行此约束。您还可以添加 putAll(TTMap)
方法,如果你愿意的话。
key == value
签到put()
允许null
只要 key
都满足和value
为空。如果只有其中之一,您将得到 NullPointerException
在类平等检查中。
另请注意@Radiodef 的评论 - 每个类型都会扩展 Object
,因此使用泛型无法阻止两种类型被强制转换为 Object
并插入。您需要额外的运行时检查来解决这个问题。
如果实现Map
不是必需的,您可以相当轻松地在新类上遵循类似的模式(私有(private) Map<Object, Object>
和类型化的 getter 和 setter)。
关于java - 从 T 到 T 的 map ,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30943271/