オブジェクト ベースのコレクションがジェネリフィケーションの最有力候補であり、コレクション ライブラリで既にジェネリフィケーションされていることを私は知っています。それ以外に、クラスまたはインターフェースがジェネリフィケーションの候補にするために、どのような属性と動作を保持する必要がありますか? たとえば、 ThreadLocal
クラスまたはAtomicReference
クラスが一般化されたのはなぜですか? この設計上の決定に至った理由は何ですか?
5 に答える
異なる型に再利用する必要があるクラスは、常にジェネリフィケーションの候補です。例えばclass Pair<U,V>
クラスが他のさまざまなクラスと同じ関係を持つことができる場合、そのクラスはジェネリフィケーションの良い候補です。コレクション クラスが生成されるのは、「含む」という関係がオブジェクトのどのクラスにも適用できるためです。「と比較できる」という関係は多くの異なるオブジェクトに適用できるため、Comparable
およびインターフェイスは汎用的です (ただし、実際には、これは のクラス独自の階層内およびその周辺のオブジェクトに限定されます)。Comparator
Comparable
さらに、非常によく似た複数のクラスを作成していて、唯一の違いが操作対象のオブジェクトのタイプであることに気付いた場合は、1 つの汎用クラスを作成することを検討してください。
編集:
組み込みクラスAtomicReference
およびを使用するとThreadLocal
、「含む」関係がさまざまなオブジェクト タイプに適用されます。AtomicReference
およびオブジェクトには、まだ別のタイプのThreadLocal
オブジェクトが含まれています。これにより、特定の型をタイプ セーフな方法で設定および返すことができます。「get」および「set」メソッドが戻り値を取得する必要はなく、「get」をObject
呼び出すときに戻り値の型をキャストする必要がなくなります。 " 方法。この関係は特定の型に適用されますが、クラスの設計時に正確な型を知る必要はありません。
編集2:
クラスをジェネリックにするために考えられるほとんどの例には、「含む」関係が含まれます。ただし、コレクション フレームワークは "contains" 関係に基づいており、"contains" で生成されたクラスを多数提供していますが、これは一般的な関係の 1 つの具体例にすぎません。一般的な関係は「含む」必要はないので、何かを一般化できるが関係が「含む」場合の例を挙げようとします。
、、Shape
などの具体的なクラスを持つ抽象クラスを想像してみてください。次に、形状を描画する方法を知っているインターフェイスを定義します。ここでの関係は「ドロー」です。インターフェイスを次のように定義できます。Line
Square
Circle
ShapeDrawer
public interface ShapeDrawer {
public void drawShape(Shape shape);
}
ここで、や などShapeDrawer
の特定の形状に特化したの具体的な実装が必要です。CircleDrawer
SquareDrawer
public class CircleDrawer implements ShapeDrawer {
public void drawShape(Shape shape) {/*...*/}
}
のdrawShape
メソッドは、描画する前にそれが であるかどうかをCircleDrawer
テストする必要があります。shape
Circle
さて、ジェネリックを使用したソリューション:
public interface ShapeDrawer<T extends Shape> {
public void drawShape(T shape);
}
これで、実装は特定の を描画できますShape
:
public class CircleDrawer implements ShapeDrawer<Circle> {
public void drawShape(Circle circle) {/*...*/}
}
構造は複数のタイプの入力を処理できる必要があるため、データ構造は汎用的である必要があります。
複数のデータ型で使用できる/使用する必要があるクラスは、一般的な方法で作成する必要があります。
「一般化」するものを探してはいけません。むしろ、ジェネリックを使用して、最適と思われる方法で特定のタスクを達成する必要があります。Generics は、タスクを実行するためのツールボックス内の単なるツールです。
あなたの質問が、おそらく一般的にどのようなものが最もよく実装されているかということであれば、あなた自身の例は最高のものです。ジェネリックは、リストのように異なる構造を同様の方法で処理する必要がある場合に適したソリューションです。これには、クラス不変の方法で他のクラスを処理する、またはクラスのサブセクションに対して不変であるほとんどのクラスが含まれます。
値をキャストしていることに気付いたときはいつでも、ジェネリフィケーションによってそのキャストを削除できるかどうかを検討する必要があります。ThreadLocal
あなたの例を見てみましょうAtomicReference
. によって返された値を使用する場合はどちらもget()
キャストが必要です (少なくともほとんどの場合)。
ジェネリックは非常にうるさい場合があることに注意してください。コストとメリットを天秤にかけてください。特に、ジェネリフィケーションが API ユーザーに利益をもたらすのか、それとも実装者だけに利益をもたらすのかを検討してください。