javagenericswildcardbounded-wildcard

Generalizing the insertion of different subclasses to respective HashMaps


I have two classes, Stock and Crypto. Both are subclasses of Security, which is where they get the methods invoked below. I want a general method that accepts either one of these and adds or updates to the respective HashMap. Something like:

1  class Portfolio {
2      private HashMap<String, Stock> stocks;
3      private HashMap<String, Crypto> cryptos;
4
5      public void add(Security s) {
6          HashMap<String, ? extends Security> table;
7          if (s instanceof Stock)
8              table = stocks;
9          else if (s instanceof Crypto)
10             table = cryptos;
11         else
12             return;
13         if (table.containsKey(s.toString()))
14             table.get(s.toString()).addShares(s.getShares(), s.getAvgPrice());
15         else
16             table.put(s.toString(), s); //ERROR
17         totalEquity += s.getAvgPrice() * s.getShares();
18     }
19 }    

I'm aware that only null can be put in a wildcard HashMap like this. I tried the helper method workaround described in the wildcard docs but I still get an error. I'd like to find a solution that doesn't require repeating lines 13-17 for the two subclasses since the only methods needed here are implemented by the superclass.


Solution

  • Given the information above, I think the easiest thing you can do is to add two public methods (one taking Stock and another taking Crypto) which simply dispatch the correct map to a generic private add method where the sub-type is inferred:

    public void add(Stock s) {
        add(s, stocks);
    }
    
    public void add(Crypto c) {
        add(c, cryptos);
    }
    
    private <T extends Security> void add(T s, Map<String, T> map) {
        map.put(s.getName(), s); //<-- put into the right map
        //or do whatever you want with it
    }
    

    With the above, you'll be able to do the following:

    portfolio.add(new Stock("APPLE")); //<-- compiler sends it to the add(Stock s) method
    portfolio.add(new Crypto("BTC")); //<-- compiler sends it to the add(Crypto c) method
    

    The compiler will dispatch to the right public method, which will simply pick the right map and pass it to the generified private add method which handles a T extends Security.