Avoiding unsafe cast for generic situation involving runtime passing of class

492 views Asked by At
public class AutoKeyMap<K,V> {

    public interface KeyGenerator<K> {
        public K generate();
    }    
    private KeyGenerator<K> generator;

    public AutoKeyMap(Class<K> keyType) {
        // WARNING: Unchecked cast from AutoKeyMap.IntKeyGen to AutoKeyMap.KeyGenerator<K>
        if (keyType == Integer.class) generator = (KeyGenerator<K>) new IntKeyGen();
        else throw new RuntimeException("Cannot generate keys for " + keyType);
    }

    public void put(V value) {
        K key = generator.generate();
        ...
    }


    private static class IntKeyGen implements KeyGenerator<Integer> {

        private final AtomicInteger ai = new AtomicInteger(1);

        @Override public Integer generate() {
            return ai.getAndIncrement();
        }

    }


}

In the code sample above, what is the correct way to prevent the given warning, without adding a @SuppressWarnings, if any?

1

There are 1 answers

0
rodion On BEST ANSWER

You can fix your code temporarily by doing this:

private KeyGenerator<?> generator;

public AutoKeyMap(Class<?> keyType) {
    // WARNING: Unchecked cast from AutoKeyMap.IntKeyGen to AutoKeyMap.KeyGenerator<K>
    if (keyType == Integer.class) generator = new IntKeyGen();
    else throw new RuntimeException("Cannot generate keys for " + keyType);
}

But if you need to implement a method in AutoKeyMap like

K generate(){
  return generator.generate();
}

then it will break again. The problem is as soon as you start doing runtime checking of class types (like you do in if(keyType == Integer.class)) then Java compiler has no way of statically ensuring that type will be correct. Because you can instantiate new AutoKeyMap<Boolean>(Class<Boolean> keyType) and then write

if(keyType == Boolean.class){
  generator = new IntKeyGen();
}

which will obviously break, and therefore the whole point of statically checked generic types will be lost.

Usually all problems like yours that involve dynamic type casting are beyond generics, so you have to live with unchecked casts, and just make sure you write enough unit tests and make sure your code works and does not throw ClassCastException in all scenarios.