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これは、インターフェースを実装するクラスのすべてのインスタンスの単一の作成ポイントです。アプリケーションを再コンパイルせずにマップの実装を変更できるようにしたいと考えています。THashMapこれにより、たとえばTroveを使用してアプリケーションを最適化できます。

問題

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<?, ?>>IMHO への参照を保持してから、 toの結果をチェックせずにキャストする方が正しいでしょ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
}
于 2013-03-20T21:51:11.880 に答える