複数の異なるタイプの値をサポートしながら、ジェネリックの利点を提供するマップを作成したいと考えています。ジェネリック コレクションの主な利点は次の 2 つだと思います。
- コレクションに間違ったものを入れる際のコンパイル時の警告
- コレクションから物を取り出すときにキャストする必要はありません
だから私が欲しいのは地図です:
- 複数の値オブジェクトをサポートし、
- マップに入れられた値をチェックします (できればコンパイル時に)
- マップから取得するときにオブジェクトの値が何であるかを知っています。
ジェネリックを使用した基本ケースは次のとおりです。
Map<MyKey, Object> map = new HashMap<MyKey, Object>();
// No type checking on put();
map.put(MyKey.A, "A");
map.put(MyKey.B, 10);
// Need to cast from get();
Object a = map.get(MyKey.A);
String aStr = (String) map.get(MyKey.A);
このキーに関連付けられた値のクラスによって生成される AbstractKey を作成することにより、2 番目の問題を解決する方法を見つけました。
public interface AbstractKey<K> {
}
public enum StringKey implements AbstractKey<String>{
A,B;
}
public enum IntegerKey implements AbstractKey<Integer>{
C,D;
}
その後、TypedMap を作成し、put() メソッドと get() メソッドをオーバーライドできます。
public class TypedMap extends HashMap<AbstractKey, Object> {
public <K> K put(AbstractKey<K> key, K value) {
return (K) super.put(key, value);
}
public <K> K get(AbstractKey<K> key){
return (K) super.get(key);
}
}
これにより、次のことが可能になります。
TypedMap map = new TypedMap();
map.put(StringKey.A, "A");
String a = map.get(StringKey.A);
ただし、キーに間違った値を入力しても、コンパイル エラーは発生しません。ClassCastException
代わりに、 get()でランタイムを取得します。
map.put(StringKey.A, 10); // why doesn't this cause a compile error?
String a = map.get(StringKey.A); // throws a ClassCastException
この .put() がコンパイル エラーを出すことができれば理想的です。現在の次善策として、ランタイムClassCastException
を put() メソッドでスローすることができます。
// adding this method to the AbstractKey interface:
public Class getValueClass();
// for example, in the StringKey enum implementation:
public Class getValueClass(){
return String.class;
}
// and override the put() method in TypedMap:
public <K> K put(AbstractKey<K> key, K value){
Object v = key.getValueClass().cast(value);
return (K) super.put(key, v);
}
これで、ClassCastException
は次のようにマップに配置されたときにスローされます。これは、間違ったキーと値の組み合わせが TypedMap に配置された場所を特定するためのデバッグをより簡単かつ迅速に行うことができるため、望ましい方法です。
map.put(StringKey.A, 10); // now throws a ClassCastException
だから、私は知りたいです:
map.put(StringKey.A, 10)
コンパイルエラーにならないのはなぜですか?値がキーのジェネリック型に関連付けられていない場合に、プットで意味のあるコンパイル エラーが発生するように、この設計をどのように適応させることができますか?
これは、私が望むものを実現するのに適した設計ですか (トップを参照)? (その他の考え/コメント/警告もいただければ幸いです...)
私が望むものを達成するために使用できる代替デザインはありますか?
編集 - 説明:
- これが悪い設計だと思われる場合、その理由を説明していただけますか?
- 値の型の例として String と Integer を使用しました。実際には、使用できるようにしたいキーと値の型のペアが多数あります。これらを 1 つのマップで使用したい - それが目的です。