Javaは、Java 5でジェネリックスを使用して型消去を導入したため、古いバージョンのJavaで機能します。互換性とのトレードオフでした。その後、互換性が失われました [1][2] [3]-バイトコードはJVMの新しいバージョンで実行できますが、以前のバージョンでは実行できません。これは最悪の選択のように見えます。型情報を失いましたが、それでも可能です。新しいバージョンのJVM用にコンパイルされたバイトコードを古いバージョンで実行しないでください。どうなりましたか?
具体的には、JVMの次のバージョンで型消去を削除できなかった技術的な理由があるかどうかを尋ねています(以前のリリースと同様に、そのバイトコードはとにかく最後のバージョンで実行できないと仮定しています)。
[3]:型消去は、本当に好きな人のために retrolambda と同様の方法でバックポートできます。
編集:後方互換性と前方互換性の定義の議論は問題を曖昧にしていると思います。
値型の特殊な実装を可能にするために、 project valhalla を使用して、将来的に消去がある程度削除される予定です。
もっと正確に言えば、型消去は実際にはジェネリックスの型の特殊化がないことを意味し、valhallaはプリミティブの特殊化を導入します。
具体的には、JVMの次のバージョンで型消去を削除できなかった技術的な理由があるかどうかを尋ねています
パフォーマンス。ジェネリック型のすべての組み合わせに特化したコードを生成する必要はありません。インスタンスまたは生成されたクラスは、型タグ、ポリモーフィックインラインキャッシュ、およびランタイム型チェック(コンパイラー生成のinstanceof
チェック)を実行する必要はありません。そして、コンパイル時のチェックを通じて、ほとんどの型安全性を得ることができます。
もちろん、多くの欠点もありますが、トレードオフはすでに行われており、JVM開発者がそのトレードオフを変更する動機となるのは何かという疑問です。
また、互換性の問題である可能性もあります。型制約が適用された場合に破損する型消去に依存することで、ジェネリックコレクションを悪用するためにチェックされていないキャストを実行するコードが存在する可能性があります。
型消去は、オンまたはオフにできる単なるバイトコード機能ではありません。
これは、ランタイム環境全体の動作方法に影響します。ジェネリッククラスのすべてのインスタンスのジェネリック型をクエリできるようにする場合は、ジェネリッククラスのオブジェクトインスタンス化ごとに、実行時のClass
表現に相当するメタ情報が作成されることを意味します。
new ArrayList<String>(); new ArrayList<Number>(); new ArrayList<Object>()
と書くと、3つのオブジェクトを作成するだけでなく、タイプを反映する3つの追加のメタオブジェクト、_ArrayList<String>
_、_ArrayList<Number>
_、および_ArrayList<Object>
_を作成する可能性があります。以前は存在しませんでした。
典型的なアプリケーションでは何千もの異なるList
署名が使用されており、それらのほとんどは、そのようなReflectionの可用性が必要な場所では使用されていないことを考慮してください(この機能がないため、現在、すべての署名がそのような反射なしで動作します)。
もちろん、これは乗算され、千の異なるジェネリックリストタイプは、実装の内部クラスを数えずに、千の異なるジェネリックイテレータタイプ、千のスプリッター、およびストリームの化身を意味します。
また、オブジェクトが割り当てられていない場所で、現在内部で型消去を検討している場所にも影響します。 Collections.emptyList()
、Function.identity()
またはComparator.naturalOrder()
などは、呼び出されるたびに同じインスタンスを返します。特定のキャプチャされたジェネリック型を反射的に検査可能にすることを主張する場合、これは機能しなくなります。だからあなたが書くなら
_List<String> list=Collections.emptyList();
List<Number> list=Collections.emptyList();
_
2つの異なるインスタンスを受け取る必要があり、それぞれがgetClass()
または将来の同等物について異なるレポートを作成します。
この能力を望んでいる人は、特定の方法について狭い視野を持っているようです。特定のパラメータが実際に2つまたは3つのタイプのいずれであるかを反射的に確認できれば素晴らしいのですが、持ち運びの重さについては決して考えないでください。数千のジェネリッククラスの潜在的に数百または数千のジェネリックインスタンス化に関するメタ情報。
これは、見返りとして何が得られるかを尋ねなければならない場所です。疑わしいコーディングスタイルをサポートする機能です(これが、Reflectionを介して検出された情報によってコードの動作を変更することです)。
これまでの答えは、型消去を削除するeasyの側面、つまり実際のインスタンスの型を内省するという欲求のみを扱っていました。実際のインスタンスには具体的なタイプがあり、報告することができます。 このコメント から ser the8472 で述べたように、型消去の削除の要求は、多くの場合、_(T)
_にキャストしたり、 _new T[]
_を介して配列を作成するか、_T.class
_を介して型変数の型にアクセスします。
これは本当の悪夢を引き起こすでしょう。型変数は、具象インスタンスの実際の型とは異なる獣です。型変数は、たとえば、に解決できます。 _? extends Comparator<? super Number>
_ 1つの(かなり単純な)例を挙げます。必要なメタ情報を提供することは、オブジェクトの割り当てがはるかに高価になるだけでなく、すべてのメソッド呼び出しがこれらの追加コストを課す可能性があることを意味します。これは、汎用クラスと実際のクラスの組み合わせについて話しているだけでなく、実際のクラスについて話しているためです。また、ネストされた汎用型であっても、可能なすべてのワイルドカードの組み合わせ。
型パラメーターの実際の型は他の型パラメーターも参照する可能性があり、型チェックを非常に複雑なプロセスに変える可能性があることに注意してください。これは、から配列を作成できる場合は、型キャストごとに繰り返す必要があるだけではありません。それ、すべてのストレージ操作はそれを繰り返さなければなりません。
重いパフォーマンスの問題に加えて、複雑さは別の問題を引き起こします。 javac
のバグ追跡リストまたはStackoverflowの関連する質問を見ると、プロセスが複雑であるだけでなく、エラーが発生しやすいことに気付くかもしれません。現在、javac
のすべてのマイナーバージョンには、ジェネリック型シグネチャマッチングに関する変更と修正が含まれており、受け入れまたは拒否される内容に影響を与えます。型キャスト、変数の割り当て、配列ストアなどの組み込みのJVM操作がこの複雑さの犠牲になること、すべてのバージョンで何が合法かどうかについて異なる考えを持っていること、またはjavac
が受け入れたものを突然拒否することを望まないことは間違いありません。ルールの不一致によるコンパイル時。
下位互換性についてのあなたの理解は間違っています。
望ましい目標は、new JVMがoldライブラリコードを正しく実行し、newコードでも変更されないようにすることです。これにより、ユーザーはJavaバージョンを、コードが記述されているよりもはるかに新しいバージョンに確実にアップグレードできます。
具体的には、JVMの次のバージョンで型消去を削除できなかった技術的な理由があるかどうかを尋ねています(以前のリリースと同様に、そのバイトコードはとにかく最後のバージョンで実行できないと仮定しています)。
下位互換性が損なわれる可能性があるためです。 (実際の下位互換性...元の質問で説明された神話的な種類ではありません。)
はい、それは技術的な理由です。下位互換性はJava言語/プラットフォームの最も重要なプロパティの1つです。少なくとも、それはOracleとOracleの有料顧客の視点です。
型消去が機能しなくなった場合、過去20年間に多くの企業が行ったJavaベースのソフトウェアへの数千億ドルの投資が突然危険にさらされます。
これを自問してください。 Sun/OracleがThread.stop()
を削除しなかったのはなぜですか?