Suppressing warnings when using a dynamic class reference

Background

The existing system creates many HashMap instances through the Generics class:

 import java.util.Map; import java.util.HashMap; public class Generics { public static <K,V> Map<K, V> newMap() { return new HashMap<K,V>(); } public static void main( String args[] ) { Map<String, String> map = newMap(); } } 

This is the only point of creation for all instances of classes that implement the Map interface. We would like to change the map implementation without recompiling the application. This will allow us to use Trove THashMap , for example, to optimize the application.

Problem

Software may not be associated with Trove THashMap due to licensing terms. Thus, it would be great if there was a way to specify the name of the card to instantiate at runtime (for those people who do not have such licensing restrictions). For instance:

 import java.util.Map; import java.util.HashMap; import gnu.trove.map.hash.THashMap; public class Generics { private String mapClassName = "java.util.HashMap"; @SuppressWarnings("unchecked") public <K,V> Map<K,V> newMap() { Map<K,V> map; try { Class<? extends Map<K,V>> c = (Class<Map<K,V>>)Class.forName( getMapClassName() ).asSubclass( Map.class ); map = c.newInstance(); } catch( Exception e ) { map = new HashMap<K,V>(); } return map; } protected String getMapClassName() { return this.mapClassName; } protected void setMapClassName( String s ) { this.mapClassName = s; } public static void main( String args[] ) { Generics g = new Generics(); Map<String, String> map = g.newMap(); System.out.printf( "Class = %s\n", map.getClass().toString() ); g.setMapClassName( "gnu.trove.map.hash.THashMap" ); map = g.newMap(); System.out.printf( "Class = %s\n", map.getClass().toString() ); } } 

Question

Is there a way to avoid @SupressWarnings annotation when compiling with -Xlint and still avoid warnings?

+4
source share
1 answer

Is there a way to avoid the @SuppressWarnings annotation when compiling with -Xlint and still avoid warnings?

No. Class.forName returns a Class<?> . The only way to assign it to Class<? extends Map<K, V>> Class<? extends Map<K, V>> - make an unverified throw. Unverified throws are sometimes required, so using @SuppressWarnings("unchecked") is acceptable here (assuming you document the reason well).

IMHO, it would be more correct to leave a link to Class<? extends Map<?, ?>> Class<? extends Map<?, ?>> and then perform an uncontrolled selection of the result from newInstance to Map<K,V> . I say this only because the Class object is a canonical representation of the raw type, so the type of the Class<? extends Map<K, V>> Class<? extends Map<K, V>> bit misleading.


This is beyond the scope of the question, but an alternative is proposed here for your solution:

 public interface MapFactory { <K, V> Map<K, V> newMap() throws Exception; } public enum HashMapFactory implements MapFactory { INSTANCE; @Override public <K, V> Map<K, V> newMap() { return new HashMap<K, V>(); } } public static final class DynamicMapFactory implements MapFactory { private final Constructor<? extends Map<?, ?>> constructor; private DynamicMapFactory(Constructor<? extends Map<?, ?>> constructor) { this.constructor = constructor; } @Override //Impl note: these checked exceptions could also be wrapped in RuntimeException public <K, V> Map<K, V> newMap() throws InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException { @SuppressWarnings("unchecked") //this is okay because the default ctor will return an empty map final Map<K, V> withNarrowedTypes = (Map<K, V>)constructor.newInstance(); return withNarrowedTypes; } public static DynamicMapFactory make(String className) throws ClassNotFoundException, NoSuchMethodException, SecurityException { @SuppressWarnings("unchecked") //Class<? extends Map> can safely be cast to Class<? extends Map<?, ?>> final Class<? extends Map<?, ?>> type = (Class<? extends Map<?, ?>>)Class.forName(className).asSubclass(Map.class); final Constructor<? extends Map<?, ?>> constructor = type.getDeclaredConstructor(); return new DynamicMapFactory(constructor); } } public static void main(String[] args) throws Exception { Map<String, Integer> map1 = HashMapFactory.INSTANCE.newMap(); Map<String, Integer> map2 = DynamicMapFactory.make("java.util.TreeMap").newMap(); System.out.println(map1.getClass()); //class java.util.HashMap System.out.println(map2.getClass()); //class java.util.TreeMap } 
+1
source

All Articles