web-dev-qa-db-ja.com

C#vs Java generics

GenericsのJava実装はC#実装ほど良くないと聞いたことがあります。構文が似ているように見えるという点で、Java実装に関して標準外であるのは何ですか、それとも宗教的な観点ですか?

114
johnc

streloksiのリンク は、違いを分解するのに非常に役立ちます。簡単で汚い要約は...

構文と使用法に関して。構文は言語間でほぼ同じです。あちこちにいくつかの癖があります(最も顕著なのは制約です)。ただし、基本的に、一方を読むことができれば、もう一方を読む/使用することができます。

ただし、最大の違いは実装にあります。

Javaは、ジェネリックを実装するために型消去の概念を使用します。要するに、基礎となるコンパイル済みクラスは実際にはジェネリックではありません。 Objectにコンパイルしてキャストします。実質的にJavaジェネリックはコンパイル時のアーティファクトであり、実行時に簡単に破壊できます。

一方、C#は、CLRのおかげで、ジェネリックをバイトコードまで実装します。 CLRは、2.0でジェネリックをサポートするために、いくつかの重大な変更を加えました。利点は、パフォーマンスの向上、深い型の安全性の検証、および反映です。

繰り返しますが、提供されている link にはさらに多くの詳細な内訳があります。

157
JaredPar

違いは、MicrosoftとSunによる設計上の決定にあります。

Javaのジェネリック は、コンパイラによって type erasure を介して実装されます。つまり、コンパイル時に型チェックが行われ、型情報が削除されます。このアプローチは、ジェネリックを使用して、レガシーコードと新しいコードとの互換性を保つために行われました。

Java Tutorials、 Generics:Type Erasure :から

ジェネリック型がインスタンス化されると、コンパイラーは型消去と呼ばれる手法によってこれらの型を変換します。コンパイラーは、クラスまたはメソッド内の型パラメーターと型引数に関連するすべての情報を削除するプロセスです。型消去により、Javaジェネリックを使用するアプリケーションは、Javaジェネリックの前に作成されたライブラリおよびアプリケーションとのバイナリ互換性を維持します。

ただし、 C#(.NET)のジェネリック を使用すると、コンパイラによる型の消去は行われず、型チェックは実行時に実行されます。これには、コンパイルされたコードで型情報が保持されるという利点があります。

ウィキペディアから:

この設計の選択は、ジェネリック型を保持したリフレクションの許可、消去の制限(ジェネリック配列を作成できないなど)の緩和などの追加機能を提供するために活用されます。これは、実行時のキャストや通常の高価なボクシング変換によるパフォーマンスの低下がないことも意味します。

「.NETジェネリックはJavaジェネリック」よりも優れている」と言うよりも、ジェネリックを実装するアプローチの違いに注目する必要があります。 .NET(バージョン2.0で導入された場合)では、ジェネリックを使用することの完全な利点を実現することが、より高い優先度でした。

35
coobird

また、 this Anders Hejlsbergとの会話も見つかりました。 Anders Hejlsbergがいくつかの追加の注意事項で述べたポイントを要約すると、Javaジェネリックは、既存のJVMとの互換性を最大限にするために作成されたものであり。 C#の場合:

  • 型の消去により、実装はすべての汎用パラメーター化された値をObjectとして強制的に表します。コンパイラはObjectとより具体的な型の間の自動キャストを提供しますが、型キャストとボクシングのパフォーマンスへの悪影響を除去しません (たとえば、Objectは特定の型にキャストされますMyClassまたはintIntegerで囲む必要がありました。C#/。NETの場合はさらに深刻になりますそれらは、ユーザー定義の値型による型消去アプローチに従いました。 Andersが言ったように:「実行効率のいずれも得られません」(具体化されたジェネリックはC#で有効になります)

  • タイプ消去により、コンパイル時に情報が利用可能になり、ランタイム中にアクセスできなくなります。以前はList<Integer>であったものが、実行時にジェネリック型パラメーターを回復する方法のない単なるListになります。 これにより、リフレクションまたは動的なコード生成シナリオを構築することが難しくなりますJava generics。最近 SO answer は、匿名クラスを介した回避方法を示していますが、トリックなしで、1つのコレクションインスタンスから要素を取得して別のコレクションインスタンスに配置するリフレクションを介して実行時にコードを生成するようなものは、動的に生成されたコードの実行中に実行時に失敗する可能性があります:これらの状況では、List<Double>List<Integer>の不一致を検出してもリフレクションは役立ちません。

しかし、ジョナサンプライアーの ブログ投稿 にリンクする回答については+1です。

5
IgorK