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 实现 Handles 两次。我能够用 C# 完成此任务,但如果不使用反射或 instanceof 和强制转换,我无法在 java 中找到解决方法。

java中有没有办法使用两个通用接口来实现Handles接口?或者也许是另一种编写 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 中做到这一点。您只能实现同一通用接口的一种具体实现。我会这样做:

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

而且,显然,你不能有两种不同的方法具有相同的签名......

于 2010-12-01T15:41:13.523 回答
1

AFAIK 你不能这样做,因为在 Java 中编译源代码时,这些都将归结为handle(Event),使方法模棱两可。

与 C# 相比,Java 在运行时期间不提供通用信息。这就是为什么它像你描述的那样工作。

您将不得不更改方法名称以使它们独一无二,例如handleAddressChangedhandleAddressDiscarded

这确实是 Java 泛型的弱点之一。

于 2010-12-01T15:41:11.620 回答
0

不幸的是没有。通常的解决方案(胖、丑、快)是创建一个Handles接口(即HandlesAddressChange, HandlesAddressDiscarded)并给每个接口一个不同的方法(handleAddressChange(...), handleAddressDiscarded())。

这样,Java 运行时就可以区分它们。

或者您可以使用匿名类。

于 2010-12-01T15:46:11.800 回答
0

这是不允许的,因为 Java 在编译期间会删除通用签名。接口方法实际上将具有签名

public void handle(Object event);

所以你有两个选择。为不同的事件实现单独的处理程序:

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

如果您不介意使用(小型)库,这是我写的一个可以解决您的问题的库:

https://github.com/bertilmuth/requirementsascode

你会建立一个这样的模型

Model.builder()
  .on(AddressChanged.class).system(this::handleAddressChanged)
  .on(AddressDiscarded.class).system(this::handleAddressDiscarded)
  .build()

并运行它。

网站上描述了如何做到这一点。

于 2021-07-14T19:56:26.353 回答