Java HashMap nested generics with wildcards -


i'm trying make hashmap of hashmap values containing hashsets of different subclasses of custom class, so:

hashmap<string, hashmap<string, hashset<? extends attackcard>>> supermap 

attackcard has subclasses such as: mage, assassin, fighter. each hashmap in supermap ever have hashsets containing single subclass of attackcard.

when try putting a

hashmap<string, hashset<assassin>> 

into supermap, compiler error: comiler error

below code error occurs:

public class cardpool {  private hashmap<string, hashmap<string, hashset<? extends attackcard>>> attackpool =     new hashmap<>();  private arraylist<auxiliarycard> 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());  } 

and here snippet of assassinpool get-method:

private hashmap<string, hashset<assassin>> pool = new hashmap<>();      public hashmap<string, hashset<assassin>> get() {         return pool;     } 

i'd comment solve problem , have wonderfully working program making attackcardpools, such assassinpool, return , contain hashsets of attackcard instead of respective subclass. i'm trying understand compilation error, :)

compilation error @ line 24: error: no suitable method found `put(string, hashmap<string,hashset<assassin>>>`  this.attackpool.put("assassins", new assassinpool(). get());  method hashmap.putp.(string, hashmap<string,hashset<? extends attackcard>>>` not applicable (actual argument `hashmap<string, hashset<assassin>>` cannot converted `hashmap<string, hashset<? extends attackcard>>` method invocation conversion) 

multi-level wildcards can bit tricky @ times, when not dealt properly. should first learn how read multi-level wildcards. need learn interpret meaning of extends , super bounds in multi-level wildcards. important concepts must first learn before starting use them, else might go mad.

interpreting multi-level wildcard:

**multi-level wildcards* should read top-down. first read outermost type. if yet again paramaterized type, go deep inside type of parameterized type. understanding of meaning of concrete parameterized type , wildcard parameterized type plays key role in understand how use them. example:

list<? extends number> list;   // wildcard parameterized type list<number> list2;            // concrete parameterized type of non-generic type list<list<? extends number>> list3;  // *concrete paramterized type* of *wildcard parameterized type*. list<? extends list<number>> list4;  // *wildcard parameterized type* 

first 2 pretty clear.

take @ 3rd one. how interpret declaration? think, type of elements can go inside list. elements capture-convertible list<? extends number>, can go inside outer list:

  • list<number> - yes
  • list<integer> - yes
  • list<double> - yes
  • list<string> - no

references:

given 3rd instantiation of list can hold above mentioned type of element, wrong assign reference list this:

list<list<? extends number>> list = new arraylist<list<integer>>();  // wrong 

the above assignment should not work, else might this:

list.add(new arraylist<float>());  // can add `arraylist<float>` right? 

so, happened? added arraylist<float> collection, supposed hold list<integer> only. give trouble @ runtime. why it's not allowed, , compiler prevents @ compile time only.

however, consider 4th instantiation of multi-level wildcard. list represents family of instantiation of list type parameters subclass of list<number>. so, following assignments valid such lists:

list4 = new arraylist<integer>();  list4 = new arraylist<double>();  

references:


relating single-level wildcard:

now might making clear picture in mind, relates invariance of generics. list<number> not list<double>, although number superclass of double. similarly, list<list<? extends number>> not list<list<integer>> though list<? extends number> superclass of list<integer>.

coming concrete problem:

you have declared map as:

hashmap<string, hashmap<string, hashset<? extends attackcard>>> supermap; 

note there 3-level of nesting in declaration. be careful. it's similar list<list<list<? extends number>>>, different list<list<? extends number>>.

now element type can add supermap? surely, can't add hashmap<string, hashset<assassin>> supermap. why? because can't this:

hashmap<string, hashset<? extends attackcard>> map = new hashmap<string, hashset<assassin>>();   // isn't valid 

you can assign hashmap<string, hashset<? extends attackcard>> map , put type of map value in supermap.

option 1:

so, 1 option modify last part of code in assassin class(i guess is) to:

private hashmap<string, hashset<? extends attackcard>> pool = new hashmap<>();  public hashmap<string, hashset<? extends attackcard>> get() {     return pool; } 

... , work fine.

option 2:

another option change declaration of supermap to:

private hashmap<string, hashmap<string, ? extends hashset<? extends attackcard>>> supermap = new hashmap<>(); 

now, able put hashmap<string, hashset<assassin>> supermap. how? think of it. hashmap<string, hashset<assassin>> capture-convertible hashmap<string, ? extends hashset<? extends attackcard>>. right? following assignment inner map valid:

hashmap<string, ? extends hashset<? extends attackcard>> map = new hashmap<string, hashset<assassin>>(); 

and hence can put hashmap<string, hashset<assassin>> in above declared supermap. , original method in assassin class work fine.


bonus point:

after solving current issue, should consider change concrete class type reference respective super interfaces. should change declaration of supermap to:

map<string, map<string, ? extends set<? extends attackcard>>> supermap; 

so can assign either hashmap or treemap or linkedhashmap, anytype supermap. also, able add hashmap or treemap values of supermap. it's important understand usage of liskov substitution principle.


Comments