web-dev-qa-db-ja.com

Javaのジェネリックスの何が問題になっていますか?

このサイトで、ジェネリックのJavaの実装を非難する投稿を何度か目にしました。今、私は正直に言って、それらを使用することに問題はなかったと言えます。ただし、ジェネリッククラスを自分で作成することは試みていません。では、Javaの一般的なサポートの問題は何ですか?

50
Michael K

Javaの一般的な実装は type erasure を使用します。つまり、厳密に型指定されたジェネリックコレクションは、実行時に実際にはObject型になります。これは、プリミティブ型をジェネリックコレクションに追加するときにボックス化する必要があることを意味するため、パフォーマンスに関する考慮事項があります。もちろん、コンパイル時の型の正確さの利点は、型の消去の一般的な愚かさ、および下位互換性へのこだわりに重点を置いています。

56
ChaosPandion

すでに提供されている回答は、Java-the-language、JVM、およびJava Class Libraryの組み合わせに集中していることに注意してください。

Java-the-languageに関する限り、Javaのジェネリックには何の問題もありません。 C#vs Java generics で説明されているように、Javaの総称は言語レベルでかなり良好です1

次善策は、JVMがジェネリックを直接サポートしないことです。これは、いくつかの主要な結果をもたらします。

  • クラスライブラリのリフレクション関連の部分は、Java-the-languageで利用可能だった完全な型情報を公開できません
  • ある程度のパフォーマンスの低下が関係している

Java-the-languageは、JVMをターゲットとして、Java Class Libraryをコアライブラリとして使用して、ユビキタスにコンパイルされているため、私が行っている区別は、経験に基づくものだと思います。

1 ワイルドカードを除いて、一般的なケースでは型の推論が決定不可能になると考えられています。これは、C#とJavaジェネリックではあまり言及されないジェネリックの大きな違いです。ありがとうございます。アンチモン。

26
Roman Starkov

通常の批判は具体化の欠如です。つまり、実行時のオブジェクトにはジェネリック引数に関する情報が含まれていません(ただし、情報はフィールド、メソッド、コンストラクター、および拡張クラスとインターフェースにまだ存在しています)。つまり、たとえば_ArrayList<String>_を_List<File>_にキャストできます。コンパイラーは警告を出しますが、_ArrayList<String>_を使用してObject参照に割り当て、それを_List<String>_にキャストした場合にも警告を出します。有利な点は、ジェネリックスではおそらくキャストを行うべきではないということです。不必要なデータがなく、もちろん下位互換性がないため、パフォーマンスが向上します。

ジェネリック引数(void fn(Set<String>)およびvoid fn(Set<File>))に基づいてオーバーロードできないと不平を言う人もいます。代わりに、より適切なメソッド名を使用する必要があります。オーバーロードは静的なコンパイル時の問題であるため、このオーバーロードは具体化を必要としないことに注意してください。

プリミティブ型はジェネリックでは機能しません。

ワイルドカードと境界は非常に複雑です。彼らは非常に便利です。 Java推奨される不変性とtell-don't-askインターフェースの場合、使用側のジェネリックではなく宣言側がより適切です。

13

次のことができないので、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
      */
    }
}

ジェネリックが実際にジェネリッククラスのパラメーターである場合に、上記のコードのすべてのクラスを処理して機能させる必要はありません。

10
davidk01
  • 目的を果たさない欠落しているジェネリックパラメーターに対するコンパイラ警告は、言語を無意味に冗長にします。例:public String getName(Class<?> klazz){ return klazz.getName();}

  • ジェネリック 配列でナイスをプレイしないでください

  • タイプ情報が失われると、反射がキャストとダクトテープの混乱になります。

5
Steve B.

他の回答もこれをある程度述べていると思いますが、あまり明確ではありません。ジェネリックの問題の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のユーザーにとって重要ではないかもしれませんが、ユーザーがインスタンスの具象Generic型をどのように宣言したかについてのファンシーなことを理解するためにリフレクションを使用するファンシーフレームワークを作成したいとしましょう。

Factory<MySpecialObject> factory = new Factory<MySpecialObject>();
MySpecialObject obj = factory.create();

MySpecialObjectのインスタンスを作成するジェネリックファクトリが必要だったとしましょう。これは、このインスタンスに対して宣言した具体的なジェネリック型だからです。 Javaがそれらを消去したため、Fell Factoryクラスはこのインスタンスに対して宣言された具象型を見つけるためにそれ自体に問い合わせることはできません。

.Netのジェネリックでは、コンパイラーがバイナリにコンパイルしたため、実行時にオブジェクトがジェネリック型であることをオブジェクトが認識するため、これを行うことができます。消去ありJavaこれはできません。

5
chubbsondubs

ジェネリックスについていくつかの良いことを言うことができますが、それは問題ではありませんでした。実行時には利用できず、配列では機能しないと文句を言うかもしれませんが、それは言及されています。

大きな心理的不快感:私は時々ジェネリックを機能させることができない状況に陥ります。 (配列は最も単純な例です。)そして、ジェネリックスがその仕事をすることができないのか、それとも私がただ愚かであるのかを理解できません。 嫌いです。ジェネリックのようなものが常に機能するはずです。 Java-the-languageを使用して自分がやりたいことができないたびに、私は知っています問題は私であり、私はそのまま続けるかどうかを知っています押して、私は最終的にそこに着きます。ジェネリックでは、しつこくなりすぎると、多くの時間を無駄にする可能性があります。

しかし、実際の問題は、ジェネリックスがあまりにも多くの複雑さを加えて、あまり利益が得られないことです。最も単純なケースでは、Appleを車を含むリストに追加することを止めることができます。ただし、ジェネリックスがないと、このエラーにより、実行時にClassCastExceptionが非常に速くスローされ、時間の無駄がほとんどありません。子供がいるチャイルドシートが付いている車を追加する場合、リストがベビーチンパンジーを含むチャイルドシートが付いている車のみに限定されるというコンパイル時の警告が必要ですか?プレーンオブジェクトインスタンスのリストは、良いアイデアのように見え始めます。

Generic'edコードは、多くの単語と文字を含み、余分なスペースを大量に消費し、読み取りに時間がかかる可能性があります。余分なコードをすべて正しく機能させるために、多くの時間を費やすことができます。そうなると、めちゃくちゃ賢くなります。私は自分の時間の数時間以上も無駄にしており、他の誰かがコードを理解できるようになるかどうか疑問に思っています。自分よりもスマートでない人や、無駄にする時間が少ない人に、メンテナンスのためにそれを渡せるようにしたいと思っています。

一方、(私はそこに少し巻き込まれ、ある程度のバランスをとる必要があると感じています)、単純なコレクションとマップを使用するといいです書き込み時に追加、書き込み、チェックが行われ、通常はコードにそれほど複雑さを加えませんifsomeoneelse コレクションまたはマップを書き込みます)。そしてJavaこれはC#よりも優れています。これまで使用したいC#コレクションのどれもジェネリックを処理していないようです(コレクションに奇妙な好みがあることは認めます)。

1
RalphChapin