5

背景

现有系统通过其类创建大量HashMap实例:Generics

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'sTHashMap来优化应用程序。

问题

THashMap由于许可条件,该软件不能与 Trove 捆绑在一起。因此,如果有一种方法可以指定地图的名称以在运行时实例化(对于那些没有此类许可限制的人),那就太好了。例如:

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并且仍然避免警告?

4

1 回答 1

1

有没有办法@SuppressWarnings在编译时避免注释-Xlint并且仍然避免警告?

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

Class<? extends Map<?, ?>>恕我直言,保留对 to 的引用然后对 to 的结果进行未经检查的newInstance转换会更正确Map<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
}
于 2013-03-20T21:51:11.880 回答