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);
});
}
}
其中 Subclass1
和 Subclass2
扩展 Superclass
,Superclass
提供 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'
时会收到错误消息
我认为这与协方差的概念有关,但我不确定如何解决这个问题。我想出了几个解决方案,都不满意:
- 我是否需要两个
update
方法,每个子类一个(如果有两个以上,这很快就会变得混乱)?
- 我是否应该将
put
移到 if (updatesAreSubclass1)
子句中,并将 updates
转换为适当的 List<Subclass>
?
我认为解决此问题的最佳方法是创建两种更新方法。一份用于 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
个对象——更糟糕的是,对吧?
我在使用以下代码时遇到错误(如代码段下方所述):
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,在 else 子句中)。
如果我将 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>
?
我认为解决此问题的最佳方法是创建两种更新方法。一份用于 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
个对象——更糟糕的是,对吧?