我使用以下代码收到错误(如下面的代码段所示):
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);
});
}
}
其中Subclass1
和Subclass2
延伸Superclass
,Superclass
提供public String getId();
。
如上所述,我在尝试定义mapToUpdate时遇到错误 - Incompatible types. Required: Map<String, foo.bar.Superclass>, Found: Map<String, foo.bar.Subclass1>
(或者子句中的子类2)。
如果我将mapToUpdate更改为Map<String, ? extends Superclass>
,则在尝试put
- Wrong 2nd argument type. Found: 'foo.bar.Superclass', required '? extends foo.bar.Superclass'
我认为这与协方差的概念有关,但我不确定如何解决问题。我提出了几个解决方案,既不令人满意:
update
方法,每个子类一个(如果有两个以上,这会很快变得混乱)?put
子句中的if (updatesAreSubclass1)
,并将updates
投射到相应的List<Subclass>
吗?答案 0 :(得分:2)
这是一个解决方案,可以使用无数个可能的子类,因为据我所知,你只是创建了一个类加上Id的地图 - &gt;超类。
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);
}
答案 1 :(得分:1)
我认为解决此问题的最佳方法是创建两种更新方法。一个用于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);
});
}
答案 2 :(得分:1)
答案 3 :(得分:1)
关于继承,你的方法没有多大意义。您有两个单独的子类映射,并希望在其中一个中添加超类实例。我建议考虑一种更合适的方法来处理这个用例。
但是,如果你想保持原样,那就可以了:
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
个对象存储在为子类定义的映射中。这应该会让您认为您的实施可能存在问题。
答案 4 :(得分:1)
你不能这样做:
mapToUpdate = mapToSubclass1;
因为那时你的代码可以继续向Subclass1
添加非mapToUpdate
对象,并且编译器将无法标记它(即,它将无法提供类型安全性)
解决这个问题的一种方法是告诉编译器“我知道我在做什么”,而不是对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);
});
}
需要注意的是,您确实需要知道自己在做什么,因为如果您使用update
= updatesAreSubclass1
致电true
,那么该列表实际上并不是{ {1}}对象,您将在运行时获得Subclass1
。
注意:您将获得ClassCastException
因为我们正在使用ClassCastException
。如果你把它排除在外,你就不会得到例外情况,但你的Collections.checkedMap
地图中会有Subclass2
个对象 - 更糟糕的是,对吗?