ここでは、ジェネリックを使用して、型の安全性を維持しながら、探している型の配列を正確に取得する方法を示します(他の回答では、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
、でわかるように、GenSet
asのインスタンスを宣言するすべての型について、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
として返されます。メソッドを呼び出す返しで表される配列のコンポーネントタイプを表すオブジェクトのオブジェクトは、例えば、(どのメソッドが呼び出されたため、場合にObject
Class
int
getComponentType
Class
Class
String.class
String[].class
null
Class
オブジェクトが配列を表していません)。
最後の文は完全に正確ではありません。呼び出すと、クラスを表すオブジェクトString[].class.getComponentType()
が返されますが、その型はではなくです。そのため、次のようなことはできません。Class
String
Class<?>
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));
}