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);
したがって、ジェネリックとして使用されるものはすべてオブジェクトに変換可能でなければならず(この例ではget(0)
はObject
を返します)、プリミティブ型はそうではありません。そのため、ジェネリックでは使用できません。
Javaでは、ジェネリックはその方法で機能します...少なくとも部分的には...言語が設計されてから数年後に言語に追加されたためです1。言語デザイナーは、既存の言語と下位互換性のある設計を考え出す必要があるため、ジェネリックのオプションで制約でしたおよびJavaクラスライブラリ =。
他のプログラミング言語(C++、C#、Adaなど)では、プリミティブ型をジェネリックのパラメーター型として使用できます。しかし、これを行うことの逆は、そのような言語のジェネリック(またはテンプレート型)の実装では、通常、各型パラメーター化のジェネリック型の別個のコピーの生成が必要になるということです。
1-ジェネリックがJava 1.0に含まれなかった理由は、時間的なプレッシャーによるものです。彼らは、Java言語を迅速にリリースして、Webブラウザーが提示する新しい市場機会を満たす必要があると感じました。ジェームズゴスリングは、時間があればジェネリックを含めることを望んでいたと述べています。これが発生した場合、Java言語はどのように見えたでしょうか。
Javaでは、後方互換性のために「型消去」を使用してジェネリックが実装されます。すべてのジェネリック型は、実行時にオブジェクトに変換されます。例えば、
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はすべてのオブジェクトのスーパークラスであり、任意のユーザー定義オブジェクトを表すことができます。
すべてのprimitivesは "Object"を継承しないため、ジェネリック型として使用できません。
FYI:Project Valhallaは上記の問題に対処しようとしています。
コレクションは、Java.lang.Object
から派生した型を必要とするように定義されています。ベースタイプは単純にそれを行いません。
Java Documentation に従って、ジェネリック型変数は、プリミティブ型ではなく参照型でのみインスタンス化できます。
これは、 Project Valhalla の下のJava 10に含まれるはずです。
In Brian Goetz 論文 State of the Specialization
プリミティブに対してジェネリックがサポートされなかった理由について 優秀な説明 があります。そして、Javaの将来のリリースでは 実装方法 です。
すべての参照インスタンス化に対して1つのクラスを生成し、プリミティブなインスタンス化をサポートしないJavaの現在の消去された実装。 (これは同種の翻訳であり、Javaのジェネリックが参照型よりも範囲が広いという制限は、参照型とプリミティブ型の操作に異なるバイトコードを使用するJVMのバイトコードセットに関する同種の翻訳の制限に由来します。)ただし、Javaで消去されたジェネリックは、動作パラメータ(ジェネリックメソッド)とデータパラメトリック(ジェネリックタイプの生およびワイルドカードのインスタンス化)の両方を提供します。
...
均一型の変換戦略が選択されました。この場合、ジェネリック型変数はバイトコードに組み込まれると境界まで消去されます。これは、クラスがジェネリックであるかどうかに関係なく、同じ名前で、メンバーシグネチャが同じである単一のクラスにコンパイルされることを意味します。型の安全性はコンパイル時に検証され、ランタイムは一般的な型システムによって制限されません。次に、Objectは最も一般的なタイプであり、プリミティブタイプには拡張されないため、ジェネリックは参照タイプに対してのみ機能するという制限を課しました。