使用动态类引用时禁止警告

Dav*_*vis 5 java generics instantiation hashmap map

背景

现有系统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();
  }
}
Run Code Online (Sandbox Code Playgroud)

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

问题

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() );
  }
}
Run Code Online (Sandbox Code Playgroud)

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

Pau*_*ora 1

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

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

Class<? extends Map<?, ?>>恕我直言,保留对 to 的引用然后对newInstanceto的结果进行未经检查的转换会更正确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
}
Run Code Online (Sandbox Code Playgroud)