Hatefiend
Hatefiend

Reputation: 3596

Generics with parameterized collections?

I'm trying to create a version of the HashMap that doesn't replace the value if a duplicate key is entered, but actually adds the two corresponding values together. The key value must be of type Number so that adding can occur. However, it doesn't seem to understand that my V is of type Number, or at least it does until I try to call super.put. It's as if the V in HashMap is not actually the same V as the one that I declared to extend Number.

What is going on here?

public class AdditiveMap<K, V extends Number> extends HashMap<K, V>
{
    @Override
    public V put(final K key, final V value)
    {
        if (containsKey(key))
            // Second param Found 'Number', required 'V'
            super.put(key, (Number)(get(key).intValue() + value.intValue()));
        else 
            super.put(key, value);
    }
}

Upvotes: 0

Views: 76

Answers (1)

Andy Turner
Andy Turner

Reputation: 140309

Don't create a Map which does this in the put method. The contract of the Map.put method is (emphasis mine):

Associates the specified value with the specified key in this map (optional operation). If the map previously contained a mapping for the key, the old value is replaced by the specified value. (A map m is said to contain a mapping for a key k if and only if m.containsKey(k) would return true.)

As such, your map would violate the contract of the interface.

You are, of course, free to add a AdditiveMap.addValuesTogether (or whatever) method. But you'd be best off just using the existing merge method:

map.merge(key, newValue, v -> v + newValue);

The main advantage of this is that this same syntax works for all numeric types (well, for int, long, float and double; you'd need a cast for short, char and byte because they are widened to int when you add them; you could avoid the explicit cast by using v -> v += newValue); but you actually can't do what you're trying to do generically (without providing a Merger<V> strategy, which can add a V to another V; and that's just very heavyweight compared to using this existing method).

Upvotes: 6

Related Questions