Generics の Java 実装は C# 実装ほど良くないと聞いたことがあります。構文が似ているという点で、Java 実装の標準以下の点は何ですか、それとも宗教的な観点からですか?
3 に答える
streloksi のリンクは、違いをうまく分解しています。簡単で汚い要約ですが...
構文と使用法に関して。構文は言語間でほぼ同じです。あちこちにいくつかの癖があります (最も顕著なのはコンストレイントです)。しかし、基本的に、一方を読むことができれば、もう一方を読んだり使用したりできる可能性があります。
ただし、最大の違いは実装にあります。
Java は、型消去の概念を使用してジェネリックを実装します。要するに、基になるコンパイル済みクラスは実際には汎用的ではありません。それらはオブジェクトとキャストにコンパイルされます。実際、Java ジェネリックはコンパイル時のアーティファクトであり、実行時に簡単に覆すことができます。
一方、C# は CLR のおかげで、ジェネリックをバイト コードに至るまで実装します。CLR は、2.0 でジェネリックをサポートするためにいくつかの重大な変更を行いました。利点は、パフォーマンスの向上、深い型の安全性の検証、およびリフレクションです。
繰り返しますが、提供されたリンクにはより詳細な内訳があります。読むことをお勧めします
この違いは、Microsoft と Sun による設計上の決定に帰着します。
Java のジェネリックは、コンパイラによる型消去によって実装されます。つまり、コンパイル時に型チェックが行われ、型情報が削除されます。このアプローチは、ジェネリックを使用してレガシー コードと新しいコードの互換性を維持するために採用されました。
Java チュートリアルのGenerics: Type Erasureから:
ジェネリック型がインスタンス化されると、コンパイラは型消去と呼ばれる手法によってそれらの型を変換します。これは、コンパイラがクラスまたはメソッド内の型パラメーターと型引数に関連するすべての情報を削除するプロセスです。型消去により、ジェネリックを使用する Java アプリケーションは、ジェネリックより前に作成された Java ライブラリおよびアプリケーションとのバイナリ互換性を維持できます。
ただし、C# (.NET)のジェネリックでは、コンパイラによる型消去はなく、型チェックは実行時に実行されます。これには、コンパイルされたコードで型情報が保持されるという利点があります。
ウィキペディアから:
この設計上の選択を利用して、ジェネリック型の保存によるリフレクションの許可や、消去の制限の一部 (ジェネリック配列を作成できないなど) の緩和などの追加機能を提供します。これは、ランタイム キャストや通常はコストのかかるボクシング変換によるパフォーマンス ヒットがないことも意味します。
「.NET ジェネリックは Java ジェネリックよりも優れている」と言うよりも、ジェネリックを実装するアプローチの違いを検討する必要があります。Java では、互換性を維持することが最優先事項であったように見えますが、.NET (バージョン 2.0 で導入されたとき) では、ジェネリックを使用することの完全な利点を実現することがより高い優先事項でした。
また、アンダース・ヘルスバーグとのこの会話も興味深いかもしれません。Anders Hejlsbergがいくつかの追加のメモで述べたポイントを要約すると、Javaジェネリックは、既存のJVMとの互換性を最大化するために作成されたため、C#で見られる実装と比べて奇妙なことがほとんどありませんでした。
型消去は、実装にすべての一般的なパラメーター化された値をとして表すように強制します
Object
。コンパイラは、Object
とより具体的な型の間の自動キャストを提供しますが、型キャストとボックス化によるパフォーマンスへの悪影響を排除しません(たとえばObject
、特定の型にキャストするMyClass
か、int
ボックス化する必要Integer
があります。これは、C#/にとってさらに深刻です。ユーザー定義の値型のために型消去アプローチに従った場合はNET)。Andersが言ったように、「実行効率はまったく得られません」(洗練されたジェネリックがC#で有効にする)型消去により、コンパイル時に情報を利用できるようになり、実行時にアクセスできなくなります。以前は、実行時にジェネリック型パラメーターを回復する方法がないものに
List<Integer>
なりました。これにより、Javaジェネリックを中心にリフレクションまたは動的コード生成シナリオを構築することが困難になります。最近のSOの回答は、匿名クラスを介してそれを回避する方法を示しています。しかし、トリックがなければ、あるコレクションインスタンスから要素を取得して別のコレクションインスタンスに配置するリフレクションを介して実行時にコードを生成するようなものは、動的に生成されたコードの実行中に実行時に失敗する可能性があります。 。List
List<Double>
List<Integer>
ただし、JonathanPryorのブログ投稿にリンクしている回答は+1です。