Java でのジェネリック配列の作成
配列は、連続するメモリ位置に格納されているアイテムのコレクションとして定義できます。ジェネリック配列はデータ型に依存せず、そのデータ型は実行時に評価されます。
ただし、Java では、配列にコンポーネントに関連付けられた情報が含まれており、実行時にこの情報がメモリの割り当てに使用されるため、Java では配列を汎用にすることはできません。
Java のオブジェクト配列とリフレクションクラス機能を使用して、配列のようなジェネリック構造をシミュレートできます。これらの方法については、以下で説明します。
Java でオブジェクト配列を使用してジェネリック配列を作成する
このアプローチでは、メンバーとしての型オブジェクトの配列が使用されます。get()
および set()
関数を使用して、配列要素を読み取り、設定します。
次のプログラムは、オブジェクト配列を使用してジェネリック配列を作成する方法を示しています。
import java.util.Arrays;
class Generic_Array<E> {
private final Object[] obj_array; // object array
public final int length;
// class constructor
public Generic_Array(int len) {
// new object array
obj_array = new Object[len];
this.len = len;
}
// get new object array(obj_array[i])
E get(int j) {
@SuppressWarnings("unchecked") final E e = (E) object_array[j];
return e;
}
// set e at new object array(obj_array[i])
void set(int j, E e) {
object_array[j] = e;
}
@Override
public String toString() {
return Arrays.toString(object_array);
}
}
class Main {
public static void main(String[] args) {
final int len = 5;
// creating an integer array
Generic_Array<Integer> int_Array = new Generic_Array(len);
System.out.print("Generic Array <Integer>:"
+ " ");
for (int i = 2; i < len; i++) int_Array.set(i, i * 2);
System.out.println(int_Array);
// creating a string array
Generic_Array<String> str_Array = new Generic_Array(len);
System.out.print("Generic Array <String>:"
+ " ");
for (int i = 0; i < len; i++) str_Array.set(i, String.valueOf((char) (i + 97)));
System.out.println(str_Array);
}
}
出力:
Generic Array <Integer>: [2, 4, 6, 8, 10]
Generic Array <String>: [a, b, c, d, e]
Java でリフレクションクラスを使用してジェネリック配列を作成する
このタイプのアプローチでは、リフレクションクラスを使用して、実行時にのみタイプが認識されるジェネリック配列を作成します。
以前のアプローチとこのアプローチの唯一の違いは、リフレクションクラスがコンストラクター自体として使用されることです。その後、リフレクションクラスは、データをコンストラクタークラスに明示的に渡すことにより、オブジェクト配列を開始します。
次のプログラムは、リフレクションを使用してジェネリック配列を作成する方法を示しています。
import java.util.Arrays;
class Generic_Array<E> {
private final E[] objArray;
public final int length;
// constructor class
public Generic_Array(Class<E> dataType, int length) {
// creatting a new array with the specified data type and length at runtime using reflection
// method.
this.objArray = (E[]) java.lang.reflect.Array.newInstance(dataType, len);
this.len = len;
}
// get element at obj_Array[i]
E get(int i) {
return obj_Array[i];
}
// assign e to obj_Array[i]
void set(int i, E e) {
obj_Array[i] = e;
}
@Override
public String toString() {
return Arrays.toString(obj_Array);
}
}
class Main {
public static void main(String[] args) {
final int len = 5;
// create array with Integer as data type
Generic_Array<Integer> int_Array = new Generic_Array(Integer.class, len);
System.out.print("Generic Array<Int>:"
+ " ");
for (int i = 2; i < len; i++) int_Array.set(i, i + 10);
System.out.println(int_Array);
// create an array with String as data type
Generic_Array<String> str_Array = new Generic_Array(String.class, len);
System.out.print("Generic Array<Str>:"
+ " ");
for (int i = 0; i < len; i++) str_Array.set(i, String.valueOf((char) (i + 65)));
System.out.println(str_Array);
}
}
出力:
Generic Array<Int>: [12, 13, 14, 15, 16]
Generic Array<Str>: [A, B, C, D, E]
ジェネリック配列は、実行時に作成された引数のタイプに対してジェネリッククラスが不明であるため、型チェックまたは明示的なメカニズムが実装されていない限り、型の安全性を提供できません。
ジェネリックを使用して上記で説明した安全性のない正確な配列が必要な場合は、以下に示すように実行できます。
import java.lang.reflect.Array;
public class Gen_Set<E> {
private E[] x;
public Gen_Set(Class<E[]> cla, int len) {
x = cla.cast(Array.newInstance(cla.getComponentType(), len));
}
public static void main(String[] args) {
Gen_Set<String> foo = new Gen_Set<String>(String[].class, 1);
String[] bar = foo.x;
foo.x[0] = "xyzzy";
String baz = foo.a[0];
}
}
このコードはコンパイル時に警告を表示しません。メインクラスでは、宣言された Gen_Set
のインスタンスのタイプを、そのタイプの配列の x に同じように割り当てることができます。つまり、配列と配列の値が正しくないタイプです。