ジェネリックでできることは次のとおりです。
interface Processor<T extends Foo> {
void process(T foo);
}
interface Foo {
Processor<? extends Foo> getProcessor();
}
それから
class SomeProcessor implements Processor<SomeFoo> {
public void process(SomeFoo foo) {
// TODO
}
}
class SomeFoo implements Foo {
public Processor<? super SomeFoo> getProcessor() {
return processor;
}
}
これは明らかに、ワイルドカードを使用した対応するものが必要であることを意味しますFoo.setProcessor()
。これは、どこかでチェックされていないキャストになることを意味します。これは言語の観点から見て安全ではなく、これを回避する方法はありません。
スーパー タイプ トークンを使用してプロセッサのインスタンス化を確認できますが、これは実行時に行われるため、コンパイル時に API の誤用を保証することはできません。できる限り最善を尽くして文書化してください。
このパスティは私の考えを示しています。Foo
メソッドが現在のインターフェイス実装でインスタンス化されたジェネリック型を返すことをインターフェイスが宣言する方法が必要になるため、この問題をモデル化してコンパイル時にタイプ セーフを実現することはできません。これは継承を壊し、Java では実行できません。
ただし、スーパー タイプ トークンを使用すると、プロセッサが適切なタイプであるかどうかを実行時に確認できます。これは、プロセッサのみがインスタンスでの呼び出しを許可されていることを API ドキュメントで明確に述べていれば、言語レベルで常に保証されます。クライアント プログラマーが従わず、不適切な型で呼び出した場合でも、クラスは実行時に例外をスローします。setProcessor()
Foo
setProcessor()
補遺: Foo をパラメータ化してはいけない理由
メリットンの回答(現在受け入れられている回答)が気に入らない理由と、型をパラメーター化しFoo
てFoo<T>
.
ソフトウェアの失敗: それは悪いことでも珍しいことでもありません。失敗は早ければ早いほど良いので、修正して損失を回避できます (通常はお金ですが、実際には誰かが気にする可能性のあるものなら何でも)。これは、今日 Java を選択する説得力のある理由の 1 つです。コンパイル時に型がチェックされるため、バグのクラス全体が本番環境に到達することはありません。
これがジェネリックの出番です。バグの別のクラス全体が除外されます。あなたのケースに戻ると、誰かが型パラメーターを追加してFoo
、コンパイル時に型の安全性を確保できるようにすることを提案しています。ただし、メリットンの実装を使用すると、コンパイラのチェックをバイパスする非論理的なコードを書くことができます。
class SomeFoo implements Foo<WrongFoo> {}
プログラムが意味論的に正しいかどうかを判断するのはコンパイラーの仕事ではないと主張する人もいるかもしれませんが、私も同意しますが、優れた言語はコンパイラーが問題を発見し、解決策を示唆することを可能にします。ここで型パラメーターを使用することは、賢明なソフトウェアを大声で叫ぶだけです。
脆弱な慣習に頼るのではなく、座って IDE を閉じ、クライアント コードが失敗しないように設計を改善する方法を考えたほうがよいでしょう。最終的に、これがソフトウェアが失敗する主な理由です。言語が厳密にまたは動的に型付けされているからではなく、開発者が API を誤解しているからです。厳密に型指定された言語を使用することは、1 つのタイプの誤用を防ぐ方法です。
あなたのインターフェースは非常に奇妙です。なぜなら、それは を定義していますが、プロセッサの設定getProcessor()
を誰が担当しているかについては何も伝えていないからです。独自のプロセッサを提供するのとまったく同じである場合、型の安全性は本当に愚かな開発者によってのみ破られます。ただし、実行時にチェックできるため (スーパー タイプ トークンを使用した私のデモを参照)、優れた開発プロセス (単体テスト) で簡単に保証できます。aまたは equalを定義しても、結論はあまり変わりません。Foo
setProcessor()
あなたが探しているAPIをJavaで記述することは不可能です-ところで、親クラスが子を知らないという継承の主要なルールを破るため、すべてのオブジェクト指向言語に同じことが当てはまると思います(これは順番にポリモーフィズムをもたらします)。(デモでほのめかしたように) ワイルドカードを使用することは、Java ジェネリックを使用できる最も近い方法であり、タイプ セーフを提供し、理解しやすいものです。
タイプ セーフティの名の下に Java の規則を強制するのではなく、プロセッサに型パラメーターを追加するだけで、適切なドキュメントと単体テストを作成することをお勧めします。