Avoiding error/warning "Unlikely argument type"

80 views Asked by At

I am trying to create a generic type that implements that Map interface.

public class CountingMap<K> implements Map<K, Double> {
...
/**
 * @see java.util.Map#containsKey(java.lang.Object)
 */
@Override
public boolean containsKey(Object key) {
    return backingMap.containsKey(key);
}
...

But this gives me an error "Unlikely argument type Object for containsKey(Object) on a Map<K,Double>". (I know this is usually just a warning, but I have my IDE configured to call this an error, because it often is (for me) and I don't want to change that setting.)

I cannot do this...

@Override
public boolean containsKey(Object key) {
    if ( key instanceof K) {
        return backingMap.containsKey((K) value);   
    } else {
        return false;
    }
}

Because the type of K is erased at runtime.

The best I can do is:

/**
 * @see java.util.Map#containsKey(java.lang.Object)
 */
@Override
public boolean containsKey(Object key) {
    @SuppressWarnings("unchecked")
    K keyVal = (K) key;
    return backingMap.containsKey(keyVal);
}

Does anyone have anything better? Something that doesn't require the @SuppressWarnings? I mean, it really will fail at runtime if someone calls containsKey with an object of the wrong type. I guess I could catch ClassCastException?

Is there an idiomatic way to do this in Java?

2

There are 2 answers

1
rzwitserloot On BEST ANSWER

t really will fail at runtime if someone calls containsKey with an object of the wrong type. I guess I could catch ClassCastException?

No, it won't. That cast does nothing. That whole 'generics are erased' thing cuts both ways, the compiler doesn't know what K is.

The correct action, as always, is to never have compiler errors, only compiler warnings, and consider all warnings as violations that must never make it to production. Exactly for reasons like this (It's also convenient when halfway through writing and e.g. wanting to run the test suite).

1
jon hanson On

Seems like the IDE rule is at fault given that the 1st code excerpt is perfectly valid Java.

Does this approach get around the rule?

@Override
public boolean containsKey(Object key) {
    final BiFunction<Map<K, Double>, Object, Boolean> containsKey = Map::containsKey;
    return containsKey.apply(backingMap, key);
}