类型安全地合并具有多种类型的Map的值;不兼容的类型java.lang.Object无法转换为?的capture#1

时间:2019-02-27 11:06:06

标签: java generics

我有一个容器类,其中有一个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),因为您不能转换为(?)

有没有解决的办法?还是更好的模式来实现我想要实现的目标?

1 个答案:

答案 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没有向编译器提示该值的类实际上应该是键的泛型类型