あなたはさまざまなことについて話します。
チュートリアルからの引用は、タイプのインスタンス化について語っています。これは、IMHO の誤った名前の概念である型消去とは関係がなく、単純に、ジェネリック型が実行時に使用できなくなったことを意味します。
しかし、コンパイル時にはそうであり、インスタンス化はコンパイル時に行われます。
あなたの質問に答えるために、「コンパイル時」は広い意味です。以下はすべてコンパイル時に発生します。
- ソースファイルを読む
- 字句解析
- 解析中
- ...
- 型チェック
- ...
- コード生成
リストは決して完全ではありません。ただし、ご覧のとおり、型チェック中に、コンパイラは型のインスタンス化を認識し、それらをチェックできます。
後でバイトコードを発行しますが、バイトコードにはジェネリックを表す方法がないため、型が「消去」されます。つまり、キャストがあちこちに挿入されます。
したがって、「コンパイル時間」は何らかの形ですべてが一度に発生する瞬間であるというあなたの仮定は正しくありません。
さらに編集:
あなたはこれらすべて (つまり、「置換」という言葉) を文字通りに受け取りすぎていると思います。確かに、コンパイラには、プログラム内のすべての項目の型、名前、およびスコープが保持されるデータ構造がいくつかあります。
ほら、原則としては非常に簡単です。
static <X> List<X> meth(X[] arr) { .... }
その後、次のようにします。
Integer arr = new Integer[100];
List<Integer> list = meth(arr);
Integer foo = list.get(1);
次に、メソッドの型をインスタンス化します。
static List<Integer> meth(Integer[] arr) { .... }
ジェネリックの要点は、それmeth
がどのタイプでも機能すると言うことです。これは、コンパイラがチェックするものです。そして、X の配列を渡すと、すべての Xに対して、X のリストが返されることがわかります。したがって、Integer[] を渡したので、結果は である必要がList<Integer>
あり、list
代入は正しいです。さらに、コンパイラは、 ** すべての X ** に対して、 a から要素を取得するとList<X>
、それが X になることを認識しています。
したがって、コンパイラは foo がInteger
. 後でコードを生成するときに、Integer へのキャストをそこに挿入します。これは、型の消去により、List.get からの戻り値が Object であるためです。
また、「置換」は、コンパイラが何らかの方法でコードを変更することを意味するものではないことに注意してください。ジェネリック型シグネチャから(おそらく一時的に)非ジェネリック型シグネチャを作成し(置換することにより、これがより良い場合は、すべての型パラメーターを実際の型に置き換えます)、これを使用して型をチェックします。
私が言うなら、それはちょうど数学のようです: a を 42 に置き換えて、方程式が真かどうかを確認してください:
+ 1 = 43
その場合、この置換が「正確にどこで」行われるかを尋ねても意味がありません。おそらくあなたの脳の中にあります。