Javaのジェネリックがクラスでは機能するのに、プリミティブ型では機能しないのはなぜですか?
たとえば、これは正常に機能します。
List<Integer> foo = new ArrayList<Integer>();
しかし、これは許可されていません:
List<int> bar = new ArrayList<int>();
Java のジェネリックは、完全にコンパイル時の構成要素です。コンパイラは、すべてのジェネリックの使用を適切な型へのキャストに変換します。これは、以前の JVM ランタイムとの下位互換性を維持するためです。
これ:
List<ClassA> list = new ArrayList<ClassA>();
list.add(new ClassA());
ClassA a = list.get(0);
(大まかに) に変わります:
List list = new ArrayList();
list.add(new ClassA());
ClassA a = (ClassA)list.get(0);
したがって、ジェネリックとして使用されるものはすべて Object に変換可能である必要があり (この例でget(0)
は を返しますObject
)、プリミティブ型はそうではありません。したがって、ジェネリックでは使用できません。
Java では、ジェネリクスは、言語が設計されてから何年も後に言語に追加されたため、少なくとも部分的にはそのように機能します1。言語設計者は、既存の言語および Java クラス ライブラリと下位互換性のある設計を考え出さなければならないため、ジェネリックの選択肢に制約がありました。
他のプログラミング言語 (C++、C#、Ada など) では、プリミティブ型をジェネリックのパラメーター型として使用できます。しかし、これを行うことの裏返しとして、そのような言語のジェネリック (またはテンプレート型) の実装では、通常、型のパラメーター化ごとにジェネリック型の個別のコピーを生成する必要があります。
1 - ジェネリックが Java 1.0 に含まれていなかった理由は、時間的なプレッシャーがあったためです。彼らは、Web ブラウザによってもたらされる新しい市場機会を満たすために、Java 言語を迅速にリリースする必要があると感じていました。James Gosling は、時間があればジェネリックを含めたかったと述べています。これが起こった場合、Java 言語がどのようになっていたかは誰にもわかりません。
Java ジェネリックでは、下位互換性のために「型消去」を使用して実装されています。すべてのジェネリック型は、実行時に Object に変換されます。例えば、
public class Container<T> {
private T data;
public T getData() {
return data;
}
}
実行時に次のように表示されます。
public class Container {
private Object data;
public Object getData() {
return data;
}
}
コンパイラは、型の安全性を確保するために適切なキャストを提供する責任があります。
Container<Integer> val = new Container<Integer>();
Integer data = val.getData()
となります
Container val = new Container();
Integer data = (Integer) val.getData()
問題は、実行時に「オブジェクト」がタイプとして選択される理由です。
答えは、Objectはすべてのオブジェクトのスーパークラスであり、任意のユーザー定義オブジェクトを表すことができるということです。
すべてのプリミティブは " Object "から継承されないため、ジェネリック型として使用することはできません。
参考までに: Project Valhalla は上記の問題に対処しようとしています。
コレクションは、 から派生した型を必要とするように定義されていますjava.lang.Object
。ベースタイプは単にそれをしません。