2013-03-20 65 views
4

背景

现有系统通过其Generics类创建HashMap实例过多时抑制警告:使用动态类引用

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(); 
    } 
} 

这是创建的用于类的所有实例的单点实现Map接口。我们希望能够在不重新编译应用程序的情况下更改地图实现。这将允许我们使用Trove的THashMap来优化应用程序。

问题

软件无法与特罗韦的THashMap捆绑因许可条件。因此,如果有一种方法可以指定要在运行时实例化的地图名称(对于那些没有这种许可限制的人员),那将是非常棒的。例如:

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()); 
    } 
} 

问题

有没有办法避免@SupressWarnings批注与-Xlint编译时,仍然避免了警告?

+2

你可能想预先计算'C'和结果存储在一个静态的(并重新计算时调用'setMapClassName') - 每次你想要一张地图时,'Class.forName'有点贵重新运行。 – 2013-03-20 19:38:05

+0

我怀疑你问的问题的答案是“不”,但我喜欢被证明是错误的。 – 2013-03-20 19:40:05

回答

1

编译-Xlint时是否有办法避免@SuppressWarnings注释,仍然避免警告?

Class.forName返回Class<?>。将其分配到Class<? extends Map<K, V>>的唯一方法是进行未经检查的强制转换。有时未经检查的强制转换是必要的,因此在这里使用@SuppressWarnings("unchecked")是可以接受的(前提是您很好地记录了原因)。

恕我直言,保留对Class<? extends Map<?, ?>>的引用,然后对newInstanceMap<K,V>进行未经检查的转换会更正确。我只是这样说的,因为Class对象是原始类型的规范表示,因此像Class<? extends Map<K, V>>这样的类型稍有误导。


这是问题的范围之内,但在这里是您的解决方案建议的替代方案:

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 
}