web-dev-qa-db-ja.com

Javaでのジェネリックインターフェイスの実装

私は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);
}

このHandlesインターフェイスを次のように実装したいと思います。

public class AddressHandler implements Handles<AddressChanged>, Handles<AddressDiscarded>{
    public void handle(AddressChanged e){}
    public void handle(AddressDiscarded e){}
}

しかし、Javaは、ジェネリックを使用してハンドルを2回実装することを許可していません。C#でこれを達成できましたが、リフレクションを使用せずにJavaで回避策を理解できませんまたはinstanceofとキャスト。

Javaに、両方の汎用インターフェースを使用してHandlesインターフェースを実装する方法はありますか?あるいは、最終結果を達成できるようにHandlesインターフェースを書き込む別の方法はありますか?

27
whistlenuts

Javaではできません。実装できるのは、同じジェネリックインターフェイスの1つの具現化だけです。代わりにこれを行います:

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){}
}
10
Amir Raminfar

@Amir Raminfarに続いて、 visitor パターンを使用できます

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){}
}
19
Stan Kurilin

いいえ、Javaにコンパイルされる同じタイプの異なる「具体的な」ジェネリック型なので、オブジェクトが実装する実際のインターフェースは次のとおりです。

public interface Handles {
    public void handle(Event event);
}

そして、明らかに、同じシグネチャを持つ2つの異なるメソッドを持つことはできません...

2
cdhowie

Javaでソースコードをコンパイルすると、これらは両方ともhandle(Event)に要約され、メソッドがあいまいになるため、これを行うことはできません。

一般的な情報は、C#とは対照的に、Javaの実行時には利用できません。それがあなたが説明するようにそこで機能する理由です。

handleAddressChangedhandleAddressDiscardedのように、メソッド名を変更して一意にする必要があります。

これは確かにJavaジェネリックの弱点の1つです。

1

Java仕様の制約のため、このような実装は機能しません。しかし、AOPまたはある種のIOC-Containerを使用することを恐れないのであれば、そのために注釈を使用できます。アスペクトまたはコンテナがメッセージングインフラストラクチャを管理し、注釈を付けるメソッドを呼び出すことができます。

まず、アノテーションを作成する必要があります。

@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){}
}
0

残念ながら違います。通常の解決策(太った、醜い、速い)は、1つのHandlesインターフェイス(つまりHandlesAddressChangeHandlesAddressDiscarded)を作成し、それぞれに異なるメソッド(handleAddressChange(...)handleAddressDiscarded())。

このように、Javaランタイムはそれらを区別することができます。

または、匿名クラスを使用することもできます。

0
Aaron Digulla

Javaはコンパイル中に一般的な署名を消去するため、これは許可されていません。インターフェイスメソッドには実際に署名があります

public void handle(Object event);

したがって、2つの選択肢があります。異なるイベントに個別のハンドラーを実装します。

public class AddressChangedHandler implements Handles<AddressChanged>{ /* ... */ }
public class AddressDiscardedHandler implements Handles<AddressDiscarded>{ /* ... */ }

または、着信イベントのタイプを確認する以外はすべてに1つのハンドラーを実装します。

public void handle(Event e){
  if (e instanceof AddressChanged) {
     handleAdressChanged(e);
  }
  else if (e instanceof AddressDiscareded) {
     handleAdressDiscarded(e);
  }
}
0
Andreas_D