Map.put 使用泛型

Map.put using generics

我在使用以下代码时遇到错误(如代码段下方所述):

public class MyClass {
  private Map<String, Subclass1> mapToSubclass1;
  private Map<String, Subclass2> mapToSubclass2;

  public void update(
      final boolean updatesAreSubclass1,
      final List<? extends Superclass> updates) {

    Map<String, Superclass> mapToUpdate;
    if (updatesAreSubclass1) {
      mapToUpdate = mapToSubclass1;
    } else {
      mapToUpdate = mapToSubclass2;
    }


    updates.stream().forEach((entity) -> {
      mapToUpdate.put(entity.getId(), entity);
    });
  }
}

其中 Subclass1Subclass2 扩展 SuperclassSuperclass 提供 public String getId();.

正如所写,我在尝试定义 mapToUpdate 时遇到错误 - Incompatible types. Required: Map<String, foo.bar.Superclass>, Found: Map<String, foo.bar.Subclass1>(或子类 2,在 else 子句中)。

如果我将 mapToUpdate 更改为 Map<String, ? extends Superclass>,我在尝试 put - Wrong 2nd argument type. Found: 'foo.bar.Superclass', required '? extends foo.bar.Superclass'

时会收到错误消息

认为这与协方差的概念有关,但我不确定如何解决这个问题。我想出了几个解决方案,都不满意:

我认为解决此问题的最佳方法是创建两种更新方法。一份用于 Subclass1,一份用于 Subclass2。原因很简单,最好有两个单一方法做一件事,而不是一个带有布尔参数的方法做两件事。

这段代码看起来不错而且更易于测试。

public void update1(final List<Subclass1> updates) {
    updates.stream().forEach((entity) -> {
        mapToSubclass1.put(entity.getId(), entity);
    });
}

public void update2(final List<Subclass2> updates) {
    updates.stream().forEach((entity) -> {
        mapToSubclass2.put(entity.getId(), entity);
    });
}

Check this

普通继承在泛型中不起作用。因此,Map<String, Subclass1> 不会从 Map<String, SuperClass>.

扩展

您的选择是显式地转换 对象

if (updatesAreSubclass1) {
    updates.stream().forEach((entity) -> {
      mapToSubclass1.put(entity.getId(), (SubClass1) entity);
    });
} else {
    updates.stream().forEach((entity) -> {
      mapToSubclass2.put(entity.getId(), (SubClass2) entity);
    });
}

你的方法在继承方面没有多大意义。您有两个独立的子类映射,并且想在其中任何一个中添加超类实例。我建议考虑一种更合适的方法来处理这个用例。

但是,如果您想保持原样,这将起到作用:

public void update(
            final boolean updatesAreSubclass1,
            final List<? extends Superclass> updates) {
  updates.stream().forEach((entity) -> {
    if(updatesAreSubclass1)
      mapToSubclass1.put(entity.getId(), (Subclass1) entity);
    else
      mapToSubclass2.put(entity.getId(), (Subclass2) entity);
    });
}

您不能在没有显式转换的情况下将 Superclass 对象存储在为子类定义的映射中。这应该会让您认为您的实施可能有问题。

这是一个解决方案,可以处理无限数量的可能子classes,因为据我所知,你只是在创建一个 class 加上 Id -> Super[ 的映射=13=].

private Map<Class,Map<String,Superclass>> map = new HashMap<>();
void update(List<? extends Superclass> l) {
    l.stream().forEach(o -> put(o));
}

public void  put(Superclass obj) {
    String id = obj.getId();
    Map<String,Superclass> submap = map.get(obj.getClass());
    if(null == submap) {
        submap = new HashMap<>();
        map.put(obj.getClass(), submap);
    }
    submap.put(id, obj);
}

public Superclass get(Class clss, String id) {
    return Optional.ofNullable(map)
            .map(m -> m.get(clss))
            .map(m2 -> m2.get(id))
            .orElse(null);
}

你不能这样做:

mapToUpdate = mapToSubclass1;

因为你的代码可以继续将非 Subclass1 对象添加到 mapToUpdate 并且编译器将无法标记它(即它无法提供类型安全)。

解决此问题的一种方法是告诉编译器 "I know what I'm doing" 不要对您的 mapToUpdate 变量使用泛型。像这样:

@SuppressWarnings("unchecked")
public void update(final boolean updatesAreSubclass1,
        final List<? extends Superclass> updates) {

    if (updates.size() == 0) {
        return;
    }

    Map mapToUpdate;
    if (updatesAreSubclass1) {
        mapToUpdate = Collections.checkedMap(mapToSubclass1, Integer.class,
                Subclass1.class);
    } else {
        mapToUpdate = Collections.checkedMap(mapToSubclass2, Integer.class,
                Subclass2.class);
    }

    updates.stream().forEach(
            (entity) -> {
                System.out.println("Adding..." + entity.toString()
                        + " to map " + mapToUpdate.toString());
                mapToUpdate.put(entity.getId(), entity);
            });
}

需要注意的是,您确实需要知道自己在做什么,因为如果您使用 updatesAreSubclass1 = true 调用 update,则列表实际上并不是一个列表Subclass1 个对象,你会在运行时得到一个 ClassCastException

注意:您会得到 ClassCastException,因为我们使用的是 Collections.checkedMap。如果你忽略它,你不会得到异常,但你会在你的 mapToSubclass1 地图中得到 Subclass2 个对象——更糟糕的是,对吧?