我有一个容器类,其中有一个Map
,其值带有多种类型。为了从外部角度确保它的类型安全,密钥本身具有通用类型,指示值的类型。
键提供了一些基本功能来查找项目并合并其值。 (对于找到的值类型,可能有不同的合并规则。)
public abstract class Key<T> {
@SuppressWarnings("unchecked")
T get(Map<Key<?>, Object> data) {
return (T) data.get(this);
}
/**
* This must be provided by the implementation.
*
* @param old the old value
* @param new the new value
* @returns the merged value
*/
abstract T merge(T old, T new);
}
Container
类如下所示。
public class Container {
private final Map<Key<?>, Object> data = new HashMap<>();
public <T> Optional<T> get(Key<T> key) {
return Optional.ofNullable(key.get(data));
}
public <T> Container set(Key<T> key, T value) {
data.put(key, value);
return this;
}
@SuppressWarnings("unchecked")
public <T> Container merge(Key<T> key, T newValue) {
data.compute(key, (k, oldValue) -> key.merge((T) oldValue, newValue));
return this;
}
public Container mergeAll(Container that) {
//this does not compile: incompatible types java.lang.Object cannot be converted to capture#1 of ?
that.data.forEach((key, value) -> this.data.merge(key, value, key::merge));
return this;
}
}
除最后一个mergeAll
方法外,所有其他方法都可以正常工作。它正在查看that
容器的所有键,并将它们的值与this
容器的值合并。
但是key::merge
不会编译为错误incompatible types java.lang.Object cannot be converted to capture#1 of ?
。
在单个merge()
方法中,我使用了强制转换为(T)
的类型。但是在这种情况下,我没有通用类型,它是?
,但是我不能做类似的事情:(key, oldValue) -> key.merge((?) oldValue, (?) value)
,因为您不能转换为(?)
。
有没有解决的办法?还是更好的模式来实现我想要实现的目标?
答案 0 :(得分:1)
您应该可以通过以下方式解决此问题:
public Container mergeAll(Container that) {
that.data.forEach((key, value) -> this.data.merge(key, value, ((Key<Object>) key)::merge));
return this;
}
在您的示例中,key
具有无限制的通配符类型Key<?>
,因此接受通用参数的方法只能接受null
以外的任何内容。由于传递给merge
的值是Object
类型的,因此可以将key
强制转换为Key<Object>
。
如果您的情况适用,另一种解决方案可能是将Key<?>
更改为Key<Object>
。
我认为不会再有比此类型更安全的类型,因为映射Map<Key<?>, Object> data
没有向编译器提示该值的类实际上应该是键的泛型类型