質問
私は誰かが答えることを望んでいたJavaジェネリックの質問を持っています。次のコードを検討してください。
public interface Event{}
public class AddressChanged implements Event{}
public class AddressDiscarded implements Event{}
public interface Handles<T extends Event>{
public void handle(T event);
}
このようなハンドルインターフェイスを実装したい:
public class AddressHandler implements Handles<AddressChanged>, Handles<AddressDiscarded>{
public void handle(AddressChanged e){}
public void handle(AddressDiscarded e){}
}
しかし、Javaは、ジェネリックを使用してハンドルを2回実装することを許可していません。私はこれをC#で達成することができましたが、反射やインスタンスとキャストを使用せずにJavaで回避策を把握することはできません。
Javaには、両方の汎用インターフェイスを使用してハンドルインターフェイスを実装する方法はありますか?それとも、最終結果を達成できるようにハンドルインターフェイスを書き込む別の方法ですか?
解決
Javaではできません。同じ汎用インターフェイスの具体的な実現のみを実装できます。代わりにこれを行います:
public class AddressHandler implements Handles<Event>{
public void handle(Event e){
if(e instanceof AddressDiscarded){
handleDiscarded(e);
} else if(e instanceof AddressChanged){
handleChanged(e);
}
}
public void handleDiscarded(AddressDiscarded e){}
public void handleChanged(AddressChanged e){}
}
他のヒント
@Amir Raminfarを追いかけると、使用できます ビジター パターン
interface Event{
void accept(Visitor v);
}
interface Visitor {
void visitAddressChanged(AddressChanged a);
void visitAddressDiscarded(AddressDiscarded a);
}
class AddressChanged implements Event{
@Override
public void accept(Visitor v) {
v.visitAddressChanged(this);
}
}
class AddressDiscarded implements Event{
@Override
public void accept(Visitor v) {
v.visitAddressDiscarded(this);
}
}
class AddressHandler implements Visitor {
void handle(Event e){
e.accept(this);
}
public void visitAddressChanged(AddressChanged e){}
public void visitAddressDiscarded(AddressDiscarded e){}
}
いいえ、Javaコンパイルの異なる「コンクリート」の一般的なタイプのため 同じタイプ. 。オブジェクトが実装する実際のインターフェイスは次のとおりです。
public interface Handles {
public void handle(Event event);
}
そして、明らかに、同一の署名を持つ2つの異なる方法を持つことはできません...
Javaでソースコードをコンパイルするとき、これらは両方ともに要約されるので、あなたはそれをすることはできません。 handle(Event)
, 、方法をあいまいにします。
C#とは対照的に、Javaでのランタイム中は一般的な情報は利用できません。それがあなたが説明するようにそれが機能する理由です。
メソッド名を変更してユニークにする必要があります。 handleAddressChanged
と handleAddressDiscarded
.
これは確かにJavaジェネリックの弱点の1つです。
残念ながら違います。通常の解決策(脂肪、醜い、速い)は、それを作成することです Handles
インターフェイス(つまり、 HandlesAddressChange
, HandlesAddressDiscarded
)そしてそれらのそれぞれに別の方法を与えます(handleAddressChange(...)
, handleAddressDiscarded()
).
そうすれば、Javaランタイムは彼らに別れを告げることができます。
または、匿名クラスを使用することもできます。
Javaはコンピレーション中に一般的な署名を消去するため、許可されていません。インターフェイスメソッドには実際に署名があります
public void handle(Object event);
したがって、2つの選択肢があります。異なるイベントに個別のハンドラーを実装するか:
public class AddressChangedHandler implements Handles<AddressChanged>{ /* ... */ }
public class AddressDiscardedHandler implements Handles<AddressDiscarded>{ /* ... */ }
または、着信イベントのタイプを確認する以外のすべてのハンドラーを実装してください。
public void handle(Event e){
if (e instanceof AddressChanged) {
handleAdressChanged(e);
}
else if (e instanceof AddressDiscareded) {
handleAdressDiscarded(e);
}
}
Java仕様の制約により、このような実装は機能しません。しかし、AOPまたはある種のIOCコンテナーを使用することを恐れていない場合は、そのために注釈を使用できます。あなたの側面やコンテナよりも、メッセージングインフラストラクチャを管理し、注釈を付けるメソッドを呼び出すことができます。
まず、注釈を作成する必要があります。
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface EventConsumer {}
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Handles{}
あなたはそのようにあなたのクラスに注釈を付けることができます:
@EventConsumer
public class AddressHandler{
@Handles
public void handle(AddressChanged e){}
@Handles
public void handle(AddressDiscarded e){}
}