Javaジェネリックとデザインパターン:ジェネリックタイプへの参照をパラメーター化することは常に悪いことですか?
-
27-10-2019 - |
質問
この質問は、私の最後に部分的に関連しています 質問.
一般的なオブジェクトのコレクションを表す一般的なクラスがあります。
public interface MyObject<N extends Number>{}
public interface MyCollecion<N extends Number> {
public foo(MyObject<N> obj)
}
私のデザインでは、これらのオブジェクトのコレクションは、抽象クラスのアプローチを通じてクライアントクラス(CollectionBuilderと呼びましょう)によって構築されます。
public interface AbstractCollectionFactory {
public abstract <N extends Number> MyCollection<MyObject<N>> createCollection(String collectionType);
}
この方法で一般的なコレクションを構築する必要があります。
public class CollectionBuilder{
AbstractCollectionFactory f;
public void buildDoubleCollection(){
MyCollection<MyObject<Double>> c = f.<Double>createCell("MyType");
}
public void buildIntegerCollection(){...}
}
OK。ここですべてOKです。 CollectionBuilderは、指定する一般的なコンクリートタイプ(この場合は2倍)を認識しており、正しく構築できます。警告なしでこれをコンパイルすることができ、すべてが正常に動作するはずです。
今、私はジェネリックと私のプログラムの設計の両方に関連する質問があります。
私のアプリケーションには、CollectionBuilderによって構築されたコレクションを使用する必要がある別のクラスがあります(このクラスのUserclassと呼びましょう)。 userclass:
- どの特定のコンクリートタイプがそのコレクションであるかを知る必要はありません(
MyCollection<MyObject<Double>>
またMyCollection<MyObject<Integer>>
). - MyCollectionインターフェイスで定義されたいくつかの方法を呼び出すこれらのコレクションでいくつかの操作を実行します。
- 一般的なタイプを追加したくありません。
説明されている状況では、悪い考えが一般的なクラスのmyCollection sideed userclassをパラメーター化しないということですか?
public UserClass{
MyCollection a; //warning
MyCollection<?> b; //no warning
public method(MyObject o){ //warning
a.foo(b); //compile
}
public method2(MyObject<?> o){
b.foo(o); //error do not compile
}
}
Javaコンパイラは、一般的なパラメーターを指定しない場合、常に警告に抗議します。とにかく内部のuserclassから、私は具体的なパラメーターを知りません(そして私はそれを知りたくありません)。 myCollectionでメソッドfooを呼び出すことを許可しないでください。
質問は次のとおりです。
- 一般的なタイプへの参照をパラメーター化することは常に悪いことですか?
- 1の回答がNOである場合、これはそれをしないための正しい状況ですか?
- 1の回答が「はい」の場合、一般的なタイプを知らずに内部ueserclassからmyCollectionメソッドを使用するにはどうすればよいですか?
- 私のは悪いデザインですか?
明確になったことを願っています。私は数日からこの問題に苦労していますが、私にはわかりません。私を助けてください。
解決
Javaコンパイラによると、はい。と この答え その理由については、多くの良い点が含まれています。個人的には、いくつかの状況で、特にの使用がある場合に同意しません
Class
関係しています(たとえば、あなたが持っている場合のようにMap<Class, Object>
)。そのような場合、常にタックする必要があります<?>
「クラス」の最後までは、不必要な退屈のように感じられ、コードをこれ以上読みやすくしません。持っているという考え
Class
私にとって、それが関連付けられているオブジェクトの種類に基づいてパラメーター化されています。のポイントClass
そのタイプに関係なく、オブジェクトに関する情報(ALA反射)に関する情報を取得するために使用できる共通のインターフェイスがあることです。しかし、私は脱線します。 Javaコンパイラとジェネリックタイプの実装者によると、使用しているだけであっても、常に参照をパラメーター化する必要があります
<?>
.適用できない。いつでもタイプ情報を宣言してから使用することはできませんが
@SuppressWarnings("unchecked")
コンパイラを幸せにするための明示的なキャストとともに。ボヘミアンの答えを参照してください。
言うべき情報は本当にありません。私の頭の上から外れていますが、「一般的なオブジェクトのコレクションを表す一般的なクラス」を持つための実際のユースケースが何であるかはわかりません。コレクションを直接使用しないのはなぜですか?
他のヒント
コードをパラメーター化する方法はほとんど常にあります。簡単に言えば、私はこのようにuserclassを入力するでしょう:
public UserClass <N extends Number> {
MyCollection<N> c;
public method(MyObject<N> o){
c.foo(o);
}
}
編集
ジェネリックでクライアントのコードをポリットすることを心配している場合は、抽象的なタイプクラスを作成し、次のようなタイプの非タイプの実装を提供できます。
public abstract UserClass <N extends Number> {
MyCollection<N> c;
public method(MyObject<N> o){
c.foo(o);
}
}
public class DoubleUserClass extends UserClass<Double> {}
クラスの肥大化を減らすために、タイプのないクラスを配置できます 中身 基本クラス:
public abstract UserClass <N extends Number> {
MyCollection<N> c;
public method(MyObject<N> o){
c.foo(o);
}
public static class DoubleImpl extends UserClass<Double> {}
public static class FloatImpl extends UserClass<Float> {}
public static class IntegerImpl extends UserClass<Integer> {}
}
そして、このようにそれらを使用します: new UserClass.DoubleImpl()
等
ジェネリックに夢中になるのは簡単です。あなたはどこかに止まらなければなりません。いいえ、一般的なタイプへの参照をパラメーター化しないことは常に悪いことだとは思わない。すべてのジェネリックを正確に正しく取得するために必要な余分な努力は、それだけの価値がない場合があります。
ただし、もちろん、可能であればジェネリックを指定する方が良いでしょう。あなたの例では、ボヘミアンが示唆するように、userclassに一般的なパラメーターを追加することができます。
あなたのコレクションが悪いデザインであるかどうかについては、ここで定義しているインターフェイス/インフラストラクチャは非常に一般的に思えます。何をしますか MyCollection
それを単純に行うことはできません java.util.List<YourClass>
?本当にaが必要ですか? MyObject
インターフェース?特定のタイプのクラスを「タグ付け」したい場合は、これらのオブジェクトを他のオブジェクトと違うものにするもののより具体的な説明を思いつくことができます Object
?
MyCollection<?> b;
public method2(MyObject<?> o){
b.foo(o);
}
帯域外の保証がある場合 b.foo(o)
安全で、この呼び出しを機能させる必要があります。ヘルパーメソッドを紹介します
<N extends Number>
void foo(MyCollection<N> collection, MyObject<?> obj)
{
@SuppressWarnings("unchecked")
MyObject<N> obj2 = (MyObject<N>)obj;
collection.foo(obj2);
}
MyCollection<?> b;
void method2(MyObject<?> o){
foo(b, o); // now works
}
キャスト MyObject<?>
に MyObject<N>
帯域外のガウランティのために安全です。それはその意味でチェックされたキャストであるため、警告を抑制するために正当化されます。
現実世界のアプリケーションでは、言語が表現できる以上にタイプの関係について多くを知っていることは珍しくありません。コンパイラよりも自分のコードについて多くを知っている場合、プログラマーは謝罪する必要はありません。
ジェネリックを含むキャストは少し難しいです。それは直感的ではありません foo(b,o)
作品。
生の種類を使用しても問題ありません MyCollection, MyObject
手間を避けるため。生のタイプを使用してはならないという警告を無視してください。それはナンセンスです。