回答:
悪い:
List<byte>
実際にに支えられてbyte[]
おり、ボクシングは必要ありません)。良い:
最大の問題は、Javaジェネリックはコンパイル時のみのものであり、実行時にそれを覆すことができるということです。C#はより多くのランタイムチェックを行うため、賞賛されています。この投稿には本当に良い議論がいくつかあり、他の議論にリンクしています。
Class
オブジェクトを渡すことができます。
主な問題は、Javaには実際には実行時にジェネリックスがないことです。これはコンパイル時の機能です。
Javaでジェネリッククラスを作成すると、 "Type Erasure"と呼ばれるメソッドを使用して、クラスからすべてのジェネリック型を実際に削除し、基本的にそれらをObjectで置き換えます。ジェネリックの上位バージョンは、コンパイラーがメソッド本体に現れるたびに、指定されたジェネリック型にキャストを挿入するだけです。
これにはマイナス面がたくさんあります。最大の1つであるIMHOは、リフレクションを使用してジェネリック型を検査できないことです。型は実際にはバイトコードでジェネリックではないため、ジェネリックとして検査することはできません。
ここでの違いの概要:http : //www.jprl.com/Blog/archive/development/2007/Aug-31.html
(1)非常に奇妙な動作につながります。私が考えることができる最良の例は、です。想定:
public class MyClass<T> {
T getStuff() { ... }
List<String> getOtherStuff() { ... }
}
次に、2つの変数を宣言します。
MyClass<T> m1 = ...
MyClass m2 = ...
今すぐ電話getOtherStuff()
:
List<String> list1 = m1.getOtherStuff();
List<String> list2 = m2.getOtherStuff();
2番目のパラメーターは、パラメーター化された型とは関係ありませんが、生の型(パラメーター化された型が提供されないことを意味する)であるため、コンパイラーによって汎用型引数が取り除かれています。
JDKからの私のお気に入りの宣言についても触れます。
public class Enum<T extends Enum<T>>
ワイルドカード(混合バッグ)以外は、.Netジェネリックの方が良いと思います。
public class Redundancy<R extends Redundancy<R>>
;)
The expression of type List needs unchecked conversion to conform to List<String>
Enum<T extends Enum<T>>
最初は奇妙/冗長に見えるかもしれませんが、実際にはかなり面白く、少なくともJava /それのジェネリックの制約内では。values()
列挙Enum
型には、notとして型付けされた要素の配列を与える静的メソッドがあり、その型はジェネリックパラメーターによって決定されますEnum<T>
。つまり、もちろん、その型付けは列挙型のコンテキストでのみ意味があり、すべての列挙型はのサブクラスなEnum
ので、が必要ですEnum<T extends Enum>
。ただし、Javaは生の型とジェネリックを混在させることを好みません。Enum<T extends Enum<T>>
、一貫性がれます。
私は本当に物議を醸す意見を捨てるつもりです。ジェネリックスは言語を複雑にし、コードを複雑にします。たとえば、文字列を文字列のリストにマップするマップがあるとします。昔は、これを次のように簡単に宣言できました。
Map someMap;
今、私はそれを次のように宣言する必要があります
Map<String, List<String>> someMap;
そして、それをいくつかのメソッドに渡すたびに、その長い長い宣言をもう一度繰り返す必要があります。私の意見では、余分なタイピングはすべて開発者の注意をそらし、彼を「ゾーン」から外します。また、コードが多くのクリフトで満たされた場合、後で戻って重要なロジックを見つけるためにすべてのクリフトをすばやくふるいにかけることが難しい場合があります。
Javaは、一般的に使用されている最も冗長な言語の1つであるという評判はすでに悪く、ジェネリックはその問題に追加されます。
そして、あなたはそのすべての余分な冗長性のために本当に何を買いますか?誰かが文字列を保持するはずのコレクションに整数を入れたとき、または誰かが整数のコレクションから文字列をプルしようとしたときに、実際に何回問題が発生しましたか?商用Javaアプリケーションの構築に携わった10年の経験の中で、これが大きなエラーの原因になることは決してありません。だから、私はあなたが余分な冗長性のために何を手に入れているのか本当にわかりません。それは本当に官僚的な手荷物として本当に私を襲います。
今、私は本当に物議を醸すつもりです。Java 1.4のコレクションの最大の問題は、どこでも型キャストする必要があることです。私はそれらの型キャストをジェネリックと同じ問題の多くを持っている余分で冗長な残骸だと考えています。だから、例えば、私はただすることはできません
List someList = someMap.get("some key");
私がしなければなりません
List someList = (List) someMap.get("some key");
もちろん、その理由は、get()がListのスーパータイプであるObjectを返すためです。したがって、型キャストなしでは割り当てを行うことはできません。繰り返しますが、そのルールが実際にどれだけあなたを買うかについて考えてください。私の経験から、それほどではありません。
Javaは、1)ジェネリックを追加していなかったが、2)スーパータイプからサブタイプへの暗黙的なキャストを許可していれば、はるかに優れていたと思います。実行時に誤ったキャストをキャッチします。それから私は定義するのが簡単だったかもしれない
Map someMap;
そして後で
List someList = someMap.get("some key");
すべての残骸がなくなり、コードにバグの大きな新しいソースを導入することは本当にないと思います。
ランタイムではなくコンパイル時であることのもう1つの副作用は、ジェネリック型のコンストラクターを呼び出せないことです。したがって、これらを使用して汎用ファクトリを実装することはできません...
public class MyClass {
public T getStuff() {
return new T();
}
}
--jeffk ++
Javaジェネリックは、コンパイル時に正しいかどうかがチェックされ、すべての型情報が削除されます(このプロセスは型消去と呼ばれます。したがって、ジェネリックList<Integer>
は、任意のクラスのオブジェクトを含むことができるraw型 non-generic List
に削減されます。
これにより、実行時に任意のオブジェクトをリストに挿入できるようになるだけでなく、ジェネリックパラメーターとして使用されている型を判別できなくなります。後者は次に
ArrayList<Integer> li = new ArrayList<Integer>();
ArrayList<Float> lf = new ArrayList<Float>();
if(li.getClass() == lf.getClass()) // evaluates to true
System.out.println("Equal");
これがwikiだったらいいので、他の人に追加したいと思いますが...
問題:
<
ですか?MyObject >
[]を拡張しますが、私は許可されていません)タイプ全体の消去の混乱を無視すると、指定されたジェネリックは機能しません。
これはコンパイルします:
List<Integer> x = Collections.emptyList();
しかし、これは構文エラーです:
foo(Collections.emptyList());
fooは次のように定義されています。
void foo(List<Integer> x) { /* method body not important */ }
したがって、式の型チェックがローカル変数に割り当てられているのか、メソッド呼び出しの実際のパラメーターに割り当てられているのかによって、式のタイプがチェックされるかどうかが決まります。クレイジーなの?
アーキテクトは機能性、使いやすさ、およびレガシーコードとの下位互換性のバランスをとろうとしていたため、Javaへのジェネリックの導入は困難な作業でした。かなり予想通り、妥協が必要でした。
Javaのジェネリックの実装が言語の複雑さを許容できないレベルに高めたと感じる人もいます(Ken Arnoldの「ジェネリックは有害と見なされる」を参照)。Angelika LangerのGenerics FAQsは、物事がいかに複雑になるかについてかなり良い考えを与えています。
Javaは、実行時にGenericsを適用せず、コンパイル時にのみ適用します。
これは、間違った型をジェネリックコレクションに追加するなど、興味深いことができることを意味します。
Javaジェネリックはコンパイル時のみであり、非ジェネリックコードにコンパイルされます。C#では、実際にコンパイルされたMSILは汎用です。Javaはまだ実行時にキャストするため、これはパフォーマンスに大きな影響を与えます。詳細はこちらをご覧ください。
Java Posse#279-Joe DarcyとAlex Buckleyへのインタビューを聞くと、彼らはこの問題について話します。これは、Reified Generics for JavaというタイトルのNeal Gafterブログ投稿にもリンクしています。
多くの人々は、ジェネリックがJavaで実装される方法によって引き起こされる制限に満足していません。具体的には、ジェネリック型パラメーターが具体化されていないことに不満があります。実行時には使用できません。ジェネリックは消去を使用して実装され、ジェネリック型パラメーターは実行時に単純に削除されます。
そのブログの投稿は、要件の移行の互換性に関する要点を強調した、古いエントリである消しゴムによるパズル:回答セクションを参照しています。
目標は、ソースコードとオブジェクトコードの両方の下位互換性、および移行の互換性を提供することでした。