1. 概要
List
2. コンパイラーはジェネリックスをどのように処理しますか?
通常、コンパイラーは次の 2 つの方法でジェネリックスを処理します。
1. コードの特殊化。ジェネリック クラスまたはジェネリック メソッドをインスタンス化すると、新しいオブジェクト コード (バイトコードまたはバイナリ コード) が生成されます。たとえば、汎用リストの場合、文字列、整数、および浮動小数点の 3 つのターゲット コードを生成する必要がある場合があります。
2.コード共有。ターゲット コードの一意のコピーのみがジェネリック クラスごとに生成され、ジェネリック クラスのすべてのインスタンスがこのターゲット コードにマップされ、必要に応じて型チェックと型変換が実行されます。
C++ のテンプレートは、典型的なコード特化の実装です。 C++ コンパイラは、各ジェネリック クラス インスタンスの実行コードを生成します。整数リストと文字列リストは、実行コードにおける 2 つの異なるタイプです。これはコードの肥大化につながりますが、経験豊富な C++ プログラマはコードの肥大化を巧みに回避できます。
コード特殊化のもう 1 つの欠点は、参照型コレクション内の要素が本質的にポインターであるため、参照型システム内のスペースの無駄であることです。種類ごとに実行コードを生成する必要はありません。これは、Java コンパイラーがジェネリックスを処理するためにコード共有を使用する主な理由でもあります。
Java コンパイラは、コード共有を通じてジェネリック型ごとに一意のバイトコード表現を作成し、ジェネリック型のインスタンスをこの一意のバイトコード表現にマップします。複数のジェネリック型インスタンスを一意のバイトコード表現にマッピングすることは、型消去によって実行されます。
3. タイプ消去とは何ですか?
型の消去とは、型パラメーターのマージを通じてジェネリック型のインスタンスを同じバイトコードに関連付けることを指します。コンパイラはジェネリック型のバイトコードを 1 つだけ生成し、そのインスタンスをこのバイトコードに関連付けます。型消去の鍵は、ジェネリック型から型パラメーターに関する情報を消去し、必要に応じて型チェックおよび型変換メソッドを追加することです。
型消去は、汎用 Java コードを通常の Java コードに変換することとして単純に理解できますが、コンパイラーはより直接的で、汎用 Java コードを通常の Java バイトコードに直接変換します。
型消去の主なプロセスは次のとおりです:
1. すべてのジェネリック パラメーターをその左端の境界 (最上位の親型) の型に置き換えます。
2. すべての型パラメータを削除します。
例えば、型消去後の
interface Comparable <A> { public int compareTo( A that); } final class NumericValue implements Comparable <NumericValue> { priva te byte value; public NumericValue (byte value) { this.value = value; } public byte getValue() { return value; } public int compareTo( NumericValue t hat) { return this.value - that.value; } } ----------------- class Collections { public static <A extends Comparable<A>>A max(Collection <A> xs) { Iterator <A> xi = xs.iterator(); A w = xi.next(); while (xi.hasNext()) { A x = xi.next(); if (w.compareTo(x) < 0) w = x; } return w; } } final class Test { public static void main (String[ ] args) { LinkedList <NumericValue> numberList = new LinkedList <NumericValue> (); numberList .add(new NumericValue((byte)0)); numberList .add(new NumericValue((byte)1)); NumericValue y = Collections.max( numberList ); } }<span style="color: #333333; font-family: Arial; font-size: 14px;"> </span>
の型は
インターフェース Comparable {
public int compareTo( Object that); } final class NumericValue implements Comparable { priva te byte value; public NumericValue (byte value) { this.value = value; } public byte getValue() { return value; } public int compareTo( NumericValue t hat) { return this.value - that.value; } public int compareTo(Object that) { return this.compareTo((NumericValue)that); } } ------------- class Collections { public static Comparable max(Collection xs) { Iterator xi = xs.iterator(); Comparable w = (Comparable) xi.next(); while (xi.hasNext()) { Comparable x = (Comparable) xi.next(); if (w.compareTo(x) < 0) w = x; } return w; } } final class Test { public static void main (String[ ] args) { LinkedList numberList = new LinkedList(); numberList .add(new NumericValue((byte)0)); , numberList .add(new NumericValue((byte)1)); NumericValue y = (NumericValue) Collections.max( numberList ); } }<span style="color: #333333; font-family: Arial; font-size: 14px;"> </span>
最初のジェネリック クラス Comparable 消去後、A は左端の境界オブジェクトに置き換えられます。 Comparable
2 番目の例では、型パラメータ >A を拡張します。A は Comparable のサブクラスである必要があります。まず、すべての型パラメータ ti の置換について説明します。一番左の境界 Comparable を削除し、パラメータ タイプ A を削除して、最終的な消去結果を取得します。
4. 型消去によって引き起こされる問題
型消去が隠された存在であるからこそ、それは多くの一般的な超自然的な問題に直接つながります。
Q1. 同じジェネリック クラスのインスタンスを使用してメソッド シグネチャを区別しますか? - いいえ!
java.util.*;
public class Erasure{ public void test(List<String> ls){ System.out.println("Sting"); } public void test(List<Integer> li){ System.out.println("Integer"); } }<span style="color: #333333; font-family: Arial; font-size: 14px;"> </span>
このクラスをコンパイルします、
参数类型明明不一样啊,一个List
Q2. 同时catch同一个泛型异常类的多个实例?——NO!
同理,如果定义了一个泛型一场类GenericException
Q3.泛型类的静态变量是共享的?——Yes!
猜猜这段代码的输出是什么?
import java.util.*; public class StaticTest{ public static void main(String[] args){ GT<Integer> gti = new GT<Integer>(); gti.var=1; GT<String> gts = new GT<String>(); gts.var=2; System.out.println(gti.var); } } class GT<T>{ public static int var=0; public void nothing(T x){} }<span style="color: #333333; font-family: Arial; font-size: 14px;"> </span>
答案是——2!由于经过类型擦除,所有的泛型类实例都关联到同一份字节码上,泛型类的所有静态变量是共享的。
五、Just remember
1.虚拟机中没有泛型,只有普通类和普通方法
2.所有泛型类的类型参数在编译时都会被擦除
3.创建泛型对象时请指明类型,让编译器尽早的做参数检查(Effective Java,第23条:请不要在新代码中使用原生态类型)
4.不要忽略编译器的警告信息,那意味着潜在的ClassCastException等着你。
更多Java泛型-类型擦除相关文章请关注PHP中文网!