このサイトで、Javaによるジェネリックの実装を非難する投稿を何度か見ました。今、私は正直に言って、私はそれらを使用することに何の問題もなかったと言うことができます。しかし、私は自分でジェネリッククラスを作成しようとしませんでした。では、Javaの汎用サポートに関する問題は何ですか?
このサイトで、Javaによるジェネリックの実装を非難する投稿を何度か見ました。今、私は正直に言って、私はそれらを使用することに何の問題もなかったと言うことができます。しかし、私は自分でジェネリッククラスを作成しようとしませんでした。では、Javaの汎用サポートに関する問題は何ですか?
回答:
Javaの一般的な実装では、型消去を使用します。つまり、厳密に型指定されたジェネリックコレクションは、実際にObject
は実行時に型になります。これには、一般的なコレクションに追加するときにプリミティブ型をボックス化する必要があるため、パフォーマンスに関するいくつかの考慮事項があります。もちろん、コンパイル時の型の正確さの利点は、型の消去の一般的な愚かさや、後方互換性への強迫観念よりも重要です。
TypeLiteral
google-guice.googlecode.com / svn / trunk / javadoc / com / google / inject /…の
new ArrayList<String>.getClass() == new ArrayList<Integer>.getClass()
template<T> T add(T v1, T v2) { return v1->add(v2); }
そこにadd
は、それらが何であっても2つのことをする関数を作成する本当に一般的な方法があり、add()
1つのパラメータで名前が付けられたメソッドが必要です。
Add
私が与えた簡単な例を実装することはできません。
すでに提供されている回答は、Java言語、JVM、およびJavaクラスライブラリの組み合わせに集中していることに注意してください。
Javaの言語に関する限り、Javaのジェネリックに問題はありません。C#vs Javaジェネリックで説明されているように、Javaのジェネリックは言語レベル1では非常に優れています。
準最適なのは、JVMがジェネリックを直接サポートしていないことです。これにはいくつかの大きな影響があります。
Java言語は、ターゲットとしてJVMを使用し、コアクラスライブラリとしてJavaクラスライブラリを使用して遍在的にコンパイルされているため、私が行っている区別は、つまらないものだと思います。
1ワイルドカードを除いて、一般的なケースでは型推論が決定不能になると考えられています。これは、C#とJavaジェネリックの大きな違いであり、あまり言及されていません。ありがとう、アンチモン。
8675309
その番号に一意の型(例:)を作成できるプログラムを作成できます。このプログラムZ8<Z6<Z7<Z5<Z3<Z0<Z9>>>>>>>
は他の型とは異なるメンバーを持ちます。C ++では、任意の入力から生成できるすべての型は、コンパイル時に生成する必要があります。
通常の批判は、具体化の欠如です。つまり、実行時のオブジェクトにはジェネリック引数に関する情報が含まれていません(ただし、情報はフィールド、メソッド、コンストラクター、拡張クラスおよびインターフェイスに存在します)。あなたは、言って、キャストができることをこれが意味ArrayList<String>
しますList<File>
。コンパイラーは警告を出しますがArrayList<String>
、Object
参照に割り当ててからにキャストした場合にも警告を出しますList<String>
。利点は、ジェネリックを使用すると、おそらくキャストを行うべきではないため、不要なデータがなくてもパフォーマンスが向上し、もちろん下位互換性があることです。
一部の人々は、一般的な引数(void fn(Set<String>)
およびvoid fn(Set<File>)
)に基づいてオーバーロードできないと不平を言っています。代わりに、より良いメソッド名を使用する必要があります。オーバーロードは静的なコンパイル時の問題であるため、このオーバーロードは具体化を必要としないことに注意してください。
プリミティブ型はジェネリックでは機能しません。
ワイルドカードと境界は非常に複雑です。彼らは非常に便利です。Javaが不変性とtell-don't-askインターフェースを好む場合、使用側ジェネリックよりも宣言側ジェネリックの方が適切です。
Object cs = new char[] { 'H', 'i' }; System.out.println(cs);
場合は、ナンセンスが印刷されます。のタイプを変更cs
するchar[]
と、が得られHi
ます。
Javaジェネリックは、次のことができないためにひどいです。
public class AsyncAdapter<Parser,Adapter> extends AsyncTask<String,Integer,Adapter> {
proptected Adapter doInBackground(String... keywords) {
Parser p = new Parser(keywords[0]); // this is an error
/* some more stuff I was hoping for but couldn't do because
the compiler wouldn't let me
*/
}
}
ジェネリックが実際にジェネリッククラスパラメーターである場合に、上記のコードのすべてのクラスを処理して動作させる必要はありません。
Parser
、ファクトリコードをさらに複雑にする必要があります。一方、「ジェネリック」が本当にジェネリックを意味するのであれば、工場やデザインパターンの名のとおりのその他のナンセンスは必要ありません。これは、動的言語で作業することを好む多くの理由の1つです。
目的を果たさない一般的なパラメータが欠落していることに対するコンパイラの警告は、言語を無意味に冗長にします。 public String getName(Class<?> klazz){ return klazz.getName();}
ジェネリックは配列ではうまく機能しません
型情報が失われると、反射がキャスティングとダクトテープの混乱になります。
HashMap
代わりに使用するための警告が表示されたときにイライラしHashMap<String, String>
ます。
他の答えはこれをある程度言ったと思うが、あまりはっきりしていない。ジェネリックの問題の1つは、リフレクションプロセス中にジェネリック型が失われることです。たとえば、次のとおりです。
List<String> arr = new ArrayList<String>();
assertTrue( ArrayList.class, arr.getClass() );
TypeVarible[] types = arr.getClass().getTypedVariables();
残念ながら、返された型は、arrのジェネリック型がStringであることを伝えることができません。それは微妙な違いですが、それは重要です。arrは実行時に作成されるため、ジェネリック型は実行時に消去されるため、それを把握することはできません。一部の人が述べたように、反射の観点からArrayList<Integer>
と同じように見えArrayList<String>
ます。
これはGenericsのユーザーにとっては重要ではないかもしれませんが、リフレクションを使用して、ユーザーがインスタンスの具体的なジェネリック型を宣言した方法についての凝った事柄を理解するためのいくつかの凝ったフレームワークを作成したいとしましょう。
Factory<MySpecialObject> factory = new Factory<MySpecialObject>();
MySpecialObject obj = factory.create();
インスタンスを作成するジェネリックファクトリが必要だったMySpecialObject
とします。これは、このインスタンスに対して宣言した具象ジェネリック型だからです。Well Factoryクラスは、Javaによって消去されたため、このインスタンスに対して宣言された具象型を見つけるために自身を問い合わせることができません。
.Netのジェネリックでは、実行時にオブジェクトがジェネリック型であることをコンパイラがバイナリにコンパイルしたためにオブジェクトが認識するため、これを行うことができます。消去では、Javaはこれを行うことができません。
ジェネリック医薬品について良いことをいくつか言うことができましたが、それは問題ではありませんでした。実行時に利用できず、配列で動作しないと文句を言うことができますが、それは言及されています。
大きな心理的迷惑: 私は時折、ジェネリックを機能させることができない状況に陥ります。(配列は最も単純な例です。)そして、ジェネリックが仕事をすることができないのか、私がただ愚かであるのかを理解することはできません。 嫌いです。 ジェネリックのようなものは常に機能するはずです。Java言語を使ってやりたいことができないときはいつでも、問題は私であり、プッシュし続ければ最終的にそこに着くことがわかります。ジェネリック医薬品を使用すると、しつこくなりすぎると、多くの時間を無駄にすることになります。
しかし、実際の問題は、ジェネリックがあまりにも多くの複雑さを追加しすぎて、利益が少なすぎることです。最も単純なケースでは、車を含むリストにリンゴを追加するのを止めることができます。いいよ しかし、ジェネリックがないと、このエラーは実行時にClassCastExceptionを非常に速くスローし、無駄な時間はほとんどありません。チャイルドシートにチャイルドシートのある車を追加する場合、リストはベビーチンパンジーを含むチャイルドシートの車のみを対象としているというコンパイル時の警告が必要ですか?プレーンオブジェクトインスタンスのリストは、良いアイデアのように見え始めます。
Generic'edコードは多くの単語と文字を持ち、多くの余分なスペースを取り、読むのにより多くの時間がかかります。余分なコードをすべて正しく動作させるのに多くの時間を費やすことができます。それが起こるとき、私はめちゃくちゃ賢く感じます。また、私は自分の時間の数時間以上を無駄にし、他の誰かがコードを理解できるようになるかどうか疑問に思う必要があります。自分よりも賢くない人や無駄な時間が少ない人に、メンテナンスのために引き渡したいと思います。
その一方で(私はそこに少し傷ついて、バランスをとる必要があると感じています)、単純なコレクションとマップを使用して、書き込み、書き込み、追加、チェック、チェックを行い、通常はあまり追加しませんコードの複雑さ(あれば 誰か 他の コレクションやマップを書き込みます)。そして、JavaはC#よりも優れています。私が使用したいC#コレクションはどれもジェネリックを扱うことはないようです。(コレクションに奇妙な趣味があることは認めます。)
Fine. But without generics this error would throw a ClassCastException really quick at run time with little time wasted.
これは、プログラムの起動と問題のコード行のヒットとの間にどれだけの実行時間が発生するかに本当に依存します。ユーザーがエラーを報告し、それを再現することができ、数分(または、最悪のシナリオ、時間または数日)を取る場合、そのコンパイル時の検証は...良く見て開始します