29

誰かが答えてくれることを望んでいた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 では、Generic を使用してハンドルを 2 回実装することは許可されていません。私は C# でこれを達成できましたが、リフレクションまたは instanceof とキャストを使用せずに Java で回避策を見つけることはできません。

両方のジェネリック インターフェイスを使用して Handles インターフェイスを実装する Java の方法はありますか? それとも、最終結果を達成できるように Handles インターフェイスを記述する別の方法でしょうか?

4

8 に答える 8

20

@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){}
}
于 2010-12-01T15:45:58.780 に答える
10

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){}
}
于 2010-12-01T15:40:58.437 に答える
2

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

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

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

于 2010-12-01T15:41:13.523 に答える
1

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

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

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

これは実際、Java ジェネリックの弱点の 1 つです。

于 2010-12-01T15:41:11.620 に答える
0

このような実装は、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){}
}
于 2010-12-03T23:28:25.063 に答える
0

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

そうすれば、Java ランタイムはそれらを区別できます。

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

于 2010-12-01T15:46:11.800 に答える
0

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);
  }
}
于 2010-12-01T15:48:03.090 に答える