Java HashMap使用通配符嵌套generics
我正在尝试制作一个hashmap值的hashmap,其中包含自定义类的不同子类的hashsets,如下所示:
HashMap<String, Hashmap<String, HashSet>> superMap
AttackCard
有子类,如: Mage
, Assassin
, Fighter
。 superMap中的每个HashMap只会包含一个包含AttackCard单个子类的AttackCard
。
当我尝试放一个
HashMap<String, HashSet>
进入superMap,我得到一个编译器错误:
下面是发生错误的代码:
public class CardPool { private HashMap<String, HashMap<String, HashSet>> attackPool = new HashMap(); private ArrayList auxiliaryPool; public CardPool() { (line 24)this.attackPool.put("assassins", new AssassinPool().get()); /* this.attackPool.put("fighters", new Fighter().getPool()); this.attackPool.put("mages", new Mage().getPool()); this.attackPool.put("marksmen", new Marksman().getPool()); this.attackPool.put("supports", new Support().getPool()); this.attackPool.put("tanks", new Tank().getPool()); */ this.auxiliaryPool = new ArrayList(new AuxiliaryCard().getPool()); }
这里有一个AssassinPool get方法的片段:
private HashMap<String, HashSet> pool = new HashMap(); public HashMap<String, HashSet> get() { return pool; }
我想评论一下,我可以很容易地解决我的问题并通过使所有的AttackCardPools(例如AssassinPool)返回并包含AttackCard的HashSets而不是它们各自的子类来完成一个非常好的工作程序。 我试图理解这个编译错误,但是:)
compilation error at line 24: error: no suitable method found for `put(String, HashMap<String,HashSet>>` this.attackPool.put("assassins", new AssassinPool(). get()); method HashMap.putp.(String, HashMap<String,HashSet>>` is not applicable (actual argument `HashMap<String, HashSet>` cannot be converted to `HashMap<String, HashSet>` by method invocation conversion)
如果处理不当, 多级通配符有时会有点棘手。 您应该首先学习如何阅读多级通配符。 然后,您需要学习解释多级通配符中的extends
和super
bounds的含义。 这些是在开始使用它们之前必须首先学习的重要概念,否则你很快就会生气。
解释多级通配符:
**应自上而下读取多级通配符*。 首先阅读最外面的类型。 如果这又是一个参数化类型,那么深入了解该参数化类型的类型。 理解具体参数化类型和通配符参数化类型的含义对于理解如何使用它们起着关键作用。 例如:
List extends Number> list; // this is wildcard parameterized type List list2; // this is concrete parameterized type of non-generic type List> list3; // this is *concrete paramterized type* of a *wildcard parameterized type*. List extends List
> list4; // this is *wildcard parameterized type*
前两个很清楚。
看看第3个。 你会如何解释这个宣言? 试想一下,该列表中可以包含哪些类型的元素。 所有捕获可转换为List extends Number>
的元素List extends Number>
List extends Number>
,可以进入外部列表:
-
List
– 是的 -
List
– 是的 -
List
– 是的 -
List
– NO
参考文献:
- JLS§5.1.10 – 捕获转换
- Java Generics常见问题解答 – Angelika Langer
- 通配符捕获
- IBM Developer Works文章 – 了解通配符捕获
鉴于list的第3个实例可以保存上面提到的元素类型,将引用分配给这样的列表是错误的:
List> list = new ArrayList>(); // Wrong
上面的赋值不起作用,否则你可能会这样做:
list.add(new ArrayList()); // You can add an `ArrayList ` right?
所以发生了什么事? 您刚刚将一个ArrayList
添加到一个集合中,该集合应该仅包含List
。 这肯定会在运行时给你带来麻烦。 这就是为什么它不被允许,编译器仅在编译时阻止它。
但是,请考虑多级通配符的第 4 个实例。 该列表表示List
的所有实例化的族,其类型参数是List
子类。 因此,以下分配对此类列表有效:
list4 = new ArrayList(); list4 = new ArrayList();
参考文献:
- 多级通配符是什么意思?
-
Collection
,> Collection
和> Collection extends Pair
之间的区别> Collection extends Pair
?>
与单级通配符有关:
现在,这可能会在你的脑海中形成一幅清晰的画面,这与仿制品的不变性有关。 List
不是List
,尽管Number
是Double
超类。 同样, List
>
List
不是>
List
即使>
List extends Number>
List extends Number>
是List
的超类。
来到具体问题:
您已将地图声明为:
HashMap>> superMap;
请注意,该声明中有3级嵌套 。 小心点 它类似于List
>>
List
,这与>>
List
>
List
。 >
现在你可以添加到superMap
所有元素类型是superMap
? 当然,你不能将一个HashMap
到superMap
。 为什么? 因为我们不能做这样的事情:
HashMap> map = new HashMap>(); // This isn't valid
你只能分配一个HashMap
HashMap
为map
,因此只将该类型的地图作为值放在superMap
。
选项1:
因此,一个选项是修改Assassin
类中的代码的最后一部分(我猜它是):
private HashMap> pool = new HashMap<>(); public HashMap> get() { return pool; }
……一切都会好起来的。
选项2:
另一个选择是将superMap
的声明superMap
为:
private HashMap>> superMap = new HashMap<>();
现在,您可以将HashMap
放到superMap
。 怎么样? 想一想。 HashMap
可以捕获转换为HashMap
HashMap
HashMap
。 对? 因此,内部地图的以下分配是有效的:
HashMap> map = new HashMap>();
因此,您可以在上面声明的superMap
放置HashMap
。 那么你在Assassin
课上的原始方法就可以了。
奖励点:
解决当前问题后,还应考虑将所有具体类类型引用更改为各自的超级接口。 您应该将superMap
的声明superMap
为:
Map>> superMap;
这样您就可以将任何类型的HashMap
或TreeMap
或LinkedHashMap
分配给superMap
。 此外,您还可以将HashMap
或TreeMap
添加为superMap
值。 理解Liskov替代原则的用法非常重要。
不要使用HashSet extends AttackCard>
HashSet extends AttackCard>
,只需在所有声明中使用HashSet
– superMap和所有添加的集合。
您仍然可以在Set
存储AttackCard
子类。
您应该使用抽象类型声明变量,而不是具体植入,即:
Map>> superMap
见Liskov替代原则
可能是协方差问题,你需要更换吗? extends
? super
? super
请参阅什么是PECS(生产者扩展消费者超级)?