0

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.

nqodonnell17
  • 15
  • 2
  • 5

1 Answers1

0

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.

Matteo NNZ
  • 11,930
  • 12
  • 52
  • 89
  • Ahhh thanks. I was so hung up on wildcards that I overlooked the convenience of overloading. Is there a preferred convention between letting the compiler determine using multiple overloaded methods (like your answer) vs. having just one `add(SuperClass obj)` which checks instanceof obj and calls the private by casting obj to the subclass? – nqodonnell17 Jul 09 '21 at 17:54
  • @nqodonnell17 generally speaking, if you can have a generic method is always better (less code to maintain). But in your specific case, you're handling Maps (which have strong typing at compile time), plus you need to pick two different maps depending on the subclass so I really don't see any other option, in your specific case, then go for overloading the methods (but still using the overload just to pick the right map, the rest can be generalized) – Matteo NNZ Jul 09 '21 at 18:47
  • i understand, i just had a trivial question about your preference between overloading, or alternatively, a single method like `add(Security s) { if (s instanceof Stock) add((Stock)s, this.stocks) else if (s instanceof Crypto) add((Crypto)s, this.crypto) }` both ways seem equally maintainable – nqodonnell17 Jul 13 '21 at 16:18
  • @nqodonnell17 I personally find overloading cleaner than a list of if. But yes, in this specific case, the difference is not huge in maintainability. – Matteo NNZ Jul 13 '21 at 16:43