ここでは、ジェネリックを使用して、型の安全性を維持しながら、探している型の配列を正確に取得する方法を示します(他の回答では、Object配列が返されるか、コンパイル時に警告が表示されます)。
import java.lang.reflect.Array;
public class GenSet<E> {
private E[] a;
public GenSet(Class<E[]> clazz, int length) {
a = clazz.cast(Array.newInstance(clazz.getComponentType(), length));
}
public static void main(String[] args) {
GenSet<String> foo = new GenSet<String>(String[].class, 1);
String[] bar = foo.a;
foo.a[0] = "xyzzy";
String baz = foo.a[0];
}
}
これは警告なしでコンパイルされmain、でわかるように、GenSetasのインスタンスを宣言するすべての型について、aその型の配列に割り当て、aその型の変数にから要素を割り当てることができます。つまり、配列配列の値は正しいタイプです。
これは、Javaチュートリアルで説明されているように、クラスリテラルをランタイムタイプトークンとして使用することで機能します。クラスリテラルは、コンパイラによってのインスタンスとして扱われますjava.lang.Class。使用するには、クラス名の後にを付け.classます。したがって、クラスを表すオブジェクトString.classとして機能しClassますString。これは、インターフェイス、列挙型、任意の次元の配列(例String[].class)、プリミティブ(例int.class)、およびキーワードvoid(例)でも機能しますvoid.class。
Classそれ自体は総称です(として宣言されClass<T>、TはClassオブジェクトが表す型を表します)。つまり、の型はString.classですClass<String>。
したがって、のコンストラクターを呼び出すときは常にGenSet、GenSetインスタンスの宣言された型の配列を表す最初の引数にクラスリテラルを渡します(たとえばString[].class、GenSet<String>)。プリミティブは型変数に使用できないため、プリミティブの配列を取得できないことに注意してください。
コンストラクター内でメソッドを呼び出すと、cast渡されたObject引数Classが、メソッドが呼び出されたオブジェクトによって表されるクラスにキャストされます。静的メソッドnewInstanceを呼び出すと、最初の引数として渡されたオブジェクトによって表される型の配列と、2番目の引数として渡されたオブジェクトによって指定された長さの配列java.lang.reflect.Arrayとして返されます。メソッドを呼び出す返しで表される配列のコンポーネントタイプを表すオブジェクトのオブジェクトは、例えば、(どのメソッドが呼び出されたため、場合にObjectClassintgetComponentTypeClassClassString.classString[].classnullClassオブジェクトが配列を表していません)。
最後の文は完全に正確ではありません。呼び出すと、クラスを表すオブジェクトString[].class.getComponentType()が返されますが、その型はではなくです。そのため、次のようなことはできません。ClassStringClass<?>Class<String>
String foo = String[].class.getComponentType().cast("bar"); // won't compile
オブジェクトClassを返すすべてのメソッドについても同様Classです。
この回答に対する Joachim Sauerのコメントについて(私は自分でコメントするのに十分な評判がありません)、キャストを使用する例T[]では、コンパイラーが型の安全性を保証できないため、警告が表示されます。
Ingoのコメントに関する編集:
public static <T> T[] newArray(Class<T[]> type, int size) {
return type.cast(Array.newInstance(type.getComponentType(), size));
}