11

次のようなシングルトンであるワイルドカード型のコレクションを持つクラスがあります。

public ObliviousClass{

    private static final ObliviousClass INSTANCE = new ObliviousClass();

    private Map<Key, Type<?>> map = new HashMap<Key, Type<?>>();

    public void putType(Key key, Type<?> type){
        map.put(type);
    }

    // returns the singleton
    public static ObliviousClass getInstance(){
        return INSTANCE;
    }

}

クライアント コードで、このコレクションにさまざまなパラメーター化された型を追加できるようにしたいと考えています。

void clientMethod(){
    ObliviousClass oc = ObliviousClass.getInstance();

    Type<Integer> intType = ...
    Type<String> stringType = ...

    oc.putType(new Key(0), intType);
    oc.putType(new Key(1), stringType);
}

ここまでは、私が理解しているように、すべて問題ありません。Type<?>ただし、クライアントは提供されたを取得できる必要もありますKey。したがって、次のようなメソッドが に追加されObliviousClassます。

public Type<?> getType(Key key){
    return map.get(key);
}

しかし、手元にあるEffective Javaのコピーで、次のように読みました。

戻り値の型としてワイルドカード型を使用しないでください。

クライアントは返された をキャストする必要があるため、問題を理解していますType<?>ObliviousClassしかし、上記のクライアント コードが機能しないため、ジェネリック型を作成したくありませんObliviousClass<T>...

私がやろうとしていることのためのより良いデザインはありますか? -私の現在の解決策は、クライアントに静的メソッドを提供することです。次の行に沿ったもの:

public static <T> void getType(ObliviousClass instance, Key key, Type<T> dest){
    dest = (Type<T>)instance.getType(key);
}

私はいろいろと調べましたが、私の混乱を完全に解消する答えを見つけることができませんでした.

4

4 に答える 4

6

特定のタイプの複数のインスタンスをマップに格納するタイプ セーフな方法を次に示します。Class重要なのは、静的型情報が消去されているため、実行時の型チェックを実行するために値を取得するときにインスタンスを提供する必要があるということです。

class ObliviousClass {

  private final Map<Key, Object> map = new HashMap<Key, Object>();

  public Object put(Key key, Object value)
  {
    return map.put(key, value);
  }

  public <T> T get(Key key, Class<? extends T> type)
  {
    return type.cast(map.get(key)); 
  }

}

使用法は次のようになります。

oc.put(k1, 42);
oc.put(k2, "Hello!");
...
Integer i = oc.get(k1, Integer.class);
String s = oc.get(k2, String.class);
Integer x = oc.get(k2, Integer.class); /* Throws ClassCastException */
于 2012-05-08T23:25:53.953 に答える
1

クラスを入力するだけです:

public ObliviousClass <T> {

    private Map<Key, Type<T>> map = new HashMap<Key, Type<T>>();

    public void putType(Key key, Type<T> type){
        map.put(type);
    }

    public Type<T> getType(Key key){
        map.get(key);
    }
}

参考までに、この時点で、委任パターンが有効になっています。

クライアント コードの例では、 と の 2 つのインスタンスを宣言する必要ありObliviousClassます。ObliviousClass<String>ObliviousClass<Integer>

編集:

タイプの混合バッグが必要な場合は、メソッドにタイプを課すことができますが、安全でないキャストについてコンパイラの警告が表示されます。

public class ObliviousClass {

    private final Map<Key, Type<?>> map = new HashMap<Key, Type<?>>();

    public void putType(Key key, Type<?> value) {
       map.put(key, value);
    }

    @SuppressWarnings("unchecked")
    public <T> Type<T> getType1(Key key, Class<T> typeClass) {
       return (Type<T>)map.get(key); 
    }

    @SuppressWarnings("unchecked")
    public <T> Type<T> getType2(Key key) {
        return (Type<T>) map.get(key);
    }
}

クライアントは、これらのメソッドへの呼び出しを次のように入力できます。

Type<Integer> x = obliviousClass.getType1(key, Integer.class);
Type<Integer> y = obliviousClass.<Integer>getType2(key);

どちらが好きかを選択して、それを使用してください。

于 2012-05-08T23:03:04.580 に答える
0

あなたのObliviousClassは、設計上、それが保持するアイテムのパラメータ化された型を知りません。したがって、タイプセーフにするために、そのような設計は避けるべきです:-\

しかし、それを維持したい場合は、まずキャストする必要があります。これを回避する方法はありません。しかし、その方法は非常にエラーが発生しやすいものです。例えば:

oc.put(k1, intType);
oc.put(k2, strType);
Type<Integer> tint = oc.get(k1, Integer.class)
Type<String>  tstr = oc.get(k1, String.class)  // typo in k2: compile fine

tstrそして最悪の場合、型の消去が原因で、 から取得したときではなく、実際に を使用したときにのみ実行時に失敗しますObliviousClass

そのため、別の方法でパラメーター化された型を追跡することで安全性を向上させることができます。たとえば、キーを失わずにタイプに関連付けることができます。

@Value // lombok
class Key<T> {
    private int index;
}

class Type<T> {}

class ObliviousClass {

    // side note: static final can be public safely
    public static final ObliviousClass instance = new ObliviousClass();

    private List<Type<?>> map = new ArrayList<>();

    public <T> Key<T> appendType(Type<T> type){
        // here, I found it nicer that obliviousClass generates and return the key
        // otherwise use:  "public <T> void appendType(key<T> key, Type<T> type)"
        // that binds parametrized type of both key and type arguments
        map.add(type);
        return new Key<>(map.size() - 1);
    }


    public <T> Type<T> get(Key<T> key){
        return (Type<T>) map.get(key.index);
    }
}

次に、次のように使用できます。

    Type<Integer> intType = new Type<>();
    Type<String>  strType = new Type<>();
    Key<Integer> k1 = ObliviousClass.instance.appendType(intType);
    Key<String>  k2 = ObliviousClass.instance.appendType(strType);

    Type<Integer> t1 = ObliviousClass.instance.get(k1);
    Type<String>  t2 = ObliviousClass.instance.get(k2);
    Type<String>  t3 = ObliviousClass.instance.get(k1); // won't compile
于 2020-02-19T12:17:38.643 に答える