Java Genericsがプリミティブ型をサポートしないのはなぜですか?


236

Javaのジェネリクスがプリミティブ型ではなくクラスで機能するのはなぜですか?

たとえば、これは正常に動作します。

List<Integer> foo = new ArrayList<Integer>();

しかし、これは許可されていません:

List<int> bar = new ArrayList<int>();

1
int i =(int)new Object(); ただし、問題なくコンパイルできます。
Sachin Verma

回答:


243

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)、プリミティブ型は変換できません。そのため、ジェネリックでは使用できません。


8
@DanyalAytekin-実際、JavaジェネリックはC ++テンプレートのようには処理されません...
Stephen C

20
Javaコンパイラーがプリミティブ型を使用する前にボックス化できないのはなぜですか?これは可能でしょうか?
vrwim 2014年

13
@vrwim-可能性があります。しかし、それは単に構文上の砂糖です。実際の問題は、ボックス化されたプリミティブを使用したJavaジェネリックスは、実際のプリミティブ型が使用されるC ++ / C#モデル...と比較して、時間と空間の両方で比較的高価であることです。
スティーブンC

6
@MauganRaええ、私はできることを知っています:)私はこれがひどいデザインであるという私の立場に立っています。うまくいけば、Java 10(または私が聞いたように)で修正され、より高次の関数でも修正されます。そのことについて私を引用しないでください。
Ced

4
@Cedは、初心者と専門家の両方に無限に害を及ぼすのは悪いデザインであることについて完全に同意します
MauganRa

37

Javaでは、ジェネリックは、少なくとも一部は...言語が設計されてから数年後に言語に追加されたため、ジェネリックと同じように機能します1。言語設計者は、既存の言語およびJavaクラスライブラリとの下位互換性のある設計を考え出す必要があるため、ジェネリックスのオプションに制約を受けていました。

その他のプログラミング言語(C ++、C#、Adaなど)では、プリミティブ型をジェネリックスのパラメーター型として使用できます。ただし、これを行うと、そのような言語のジェネリック(またはテンプレートタイプ)の実装では、通常、各タイプのパラメーター化に対してジェネリックタイプの個別のコピーが生成されます。


1-ジェネリックがJava 1.0に含まれなかった理由は、時間のプレッシャーが原因でした。彼らは、Webブラウザーによってもたらされる新しい市場機会を満たすために、Java言語を迅速にリリースする必要があると感じていました。ジェームズ・ゴスリング氏は、ジェネリック医薬品に時間があれば、ジェネリック医薬品を含めたいと述べた。これが起こった場合、Java言語はどのように見えたでしょうか。


11

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()

ここで問題は、なぜ「オブジェクト」が実行時にタイプとして選択されるのかということです。

Answer is Objectはすべてのオブジェクトのスーパークラスであり、ユーザー定義のオブジェクトを表すことができます。

すべてのプリミティブは「Object」を継承しないため、ジェネリック型として使用することはできません。

参考:プロジェクトValhallaは上記の問題に対処しようとしています。


プラス1は適切な命名法です。
Drazen Bjelovuk

7

コレクションは、から派生するタイプを必要とするように定義されていますjava.lang.Object。ベースタイプは単にそれをしません。


26
ここでの質問は「なぜ」だと思います。ジェネリックにオブジェクトが必要なのはなぜですか?コンセンサスは、それがデザインの選択ではなく、下位互換性への洞窟であるということです。私の目には、ジェネリックスがプリミティブを処理できない場合、それは機能不足です。現状では、プリミティブに関係するすべてのものをプリミティブごとに記述する必要があります。Comparator<t、t>の代わりに、Integer.compare(int a、int b)、Byte.compare(byte a、byte b)などがあります。それは解決策ではありません!
John P

1
ええ、プリミティブ型を超えるジェネリックは必須の機能です。ここでの提案へのリンクがあるopenjdk.java.net/jeps/218
カラスは

5

あたりとしてJavaドキュメント、ジェネリック型の変数は、参照型ではなく、プリミティブ型でインスタンス化することができます。

これは、Java 10のProject Valhallaで提供される予定です。

ブライアン・ゲッツの上の紙専門の州

ジェネリックがプリミティブでサポートされなかった理由については、優れた説明があります。また、Javaの将来のリリースでどのように実装されるか

すべての参照インスタンス化に対して1つのクラスを生成し、プリミティブインスタンス化をサポートしない、Javaの現在の消去済み実装。(これは同種の変換であり、Javaのジェネリックが参照型にのみ及ぶことができるという制限は、参照型とプリミティブ型の操作に異なるバイトコードを使用するJVMのバイトコードセットに関する同種の変換の制限によるものです。)ただし、Javaで消去されたジェネリックは、動作パラメトリック性(ジェネリックメソッド)とデータパラメトリック性(ジェネリック型の生およびワイルドカードのインスタンス化)の両方を提供します。

...

同種の変換戦略が選択されました。この場合、ジェネリック型の変数は、バイトコードに組み込まれるときに境界まで消去されます。これは、クラスがジェネリックであるかどうかにかかわらず、同じ名前で単一のクラスにコンパイルされ、メンバーシグネチャが同じであることを意味します。型の安全性はコンパイル時に検証され、ランタイムはジェネリック型システムによって制限されません。次に、Objectは利用可能な最も一般的な型であり、プリミティブ型には拡張されないため、ジェネリックは参照型に対してのみ機能するという制限が課されました。


0

オブジェクトを作成する場合、typeパラメーターをプリミティブ型で置き換えることはできません。この制限の理由については、コンパイラーの実装の問題です。プリミティブ型には、仮想マシンスタックにロードおよび格納するための独自のバイトコード命令があります。したがって、プリミティブジェネリックをこれらの個別のバイトコードパスにコンパイルすることは不可能ではありませんが、コンパイラーが複雑になります。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.