要するに、これはコンパイルされません:
public <A> void test() {
A[] temp = new A[]{};
}
それは下位互換性の問題によるものですか、それともそれを妨げる言語設計の基本的なものですか?
要するに、これはコンパイルされません:
public <A> void test() {
A[] temp = new A[]{};
}
それは下位互換性の問題によるものですか、それともそれを妨げる言語設計の基本的なものですか?
肝心なのは、配列を表すクラスがコンポーネントの型を認識している必要があるということです。したがって、Class オブジェクトのメソッドは次のようになります。
public Class<?> getComponentType()
Returns the Class representing the component type of an array. If this class does not represent an array class this method returns null.
だからあなたがしようとするとき:
A[] a = new A[0];
コンパイル時には、型がわからないのは明らかです。これはジェネリック パラメーターだからです。実行時には、型消去のために型がわかりません。したがって、配列のインスタンス化は不可能です。
上記のステートメントは、次と同等と考えてください。
A[] a = (A[])Array.newInstance(???, 0);
また、型消去により、実行時に A のクラスを取得できません。
なぜコンパイラを Object[] や Number[] などに縮小しないのかと尋ねられました。
コンポーネントの種類によって返される Class が異なるためです。そう:
new Object[0].getClass()
new Integer[0].getClass()
は同じクラスではありません。特に、クラスの「getComponentType()」メソッドは異なる値を返します。
したがって、A[] の代わりに Object[] に減らすと、実際には A[] 型の何かが返されるわけではなく、Object[] が返されます。Object[] は Integer[] にケース化できず、ClassCastException が発生します。
Java では、配列とジェネリックの型システムに互換性がありません。不一致の主な領域は 2 つあります。動的対静的型チェックと 共分散です。
ジェネリックは静的にチェックされます。つまり、コンパイラは型定義が一貫していることを確認します。「型消去」は、JVM の下位互換性を確保するための妥協案でした。コンパイル後、ジェネリック型定義は使用できなくなります。例えばList<T>
となりList
ます。
対照的に、配列は動的に型チェックされます。次の例を検討してください。
String strings[] = {"a","b","c"};
Object simple[] = strings;
simple[0] = new Object(); // Runtime error -> java.lang.ArrayStoreException
共分散は、コンテンツに基づくコンテナーの継承関係です。タイプ A、B およびコンテナ C が与えられた場合、B isAssignableFrom(A) => C isAssignable C の場合。
前の例では、Java の配列は共変です。Class<Object>.isAssignableFrom(Class<String>) => Object[] is assignable from String[]
対照的に、ジェネリック型は Java では共変ではありません。同じ例を使用します。
List<String> stringList = new ArrayList<String>();
List<Object> objectList = stringList; // compiler error - this is not allowed.
ジェネリック実装の型消去を考えると、変換時に型情報が失われるため、ジェネリック型の配列を作成できる場合、動的型チェックが損なわれます。
これらの問題の複雑さと意味についてさらに読んでください: Java Generics の配列
これは、機能しないのと同じ(またはほぼ同じ)理由で機能しnew A()
ません。コンパイラがランタイムタイプを知っていることを期待していますがA
、これは明らかにわかりません。これは、JavaGenericsがC++テンプレートのようであり、を含むテンプレートのインスタンス化ごとに新しいコードが生成される場合に機能しますA
。
はい、根本的な理由があります。それは、型消去に要約されます。
次のスニペットについて考えてみます。
A[] temp = new A[5]; // Assume this would compile.
Object[] objects = temp; // This is allowed, since A extends Object.
objects[0] = new String(); // This does *not* throw an ArrayStoreException
// due to type erasure since the original type of A
// is now Object.
A t = temp[0]; // Now this seemingly innocent line would *fail*.
関連する質問: