Java 如何为泛型类创建类型安全工厂?

Java 如何为泛型类创建类型安全工厂?,java,generics,Java,Generics,[注:我已经查看了所有可能的重复项-没有一个是直接重复项,也没有一个提供此特定问题的答案。] 我想为事件处理程序创建一个工厂,其中事件处理程序的泛型类型参数是事件类型事件类型仅在运行时已知 在代码中,这是我想要实现的一个示例: public interface Event { } public interface EventHandler<T extends Event> { void handle(T event); } public class EventHandle

[注:我已经查看了所有可能的重复项-没有一个是直接重复项,也没有一个提供此特定问题的答案。]

我想为事件处理程序创建一个工厂,其中事件处理程序的泛型类型参数是事件类型事件类型仅在运行时已知

在代码中,这是我想要实现的一个示例:

public interface Event {
}

public interface EventHandler<T extends Event> {
    void handle(T event);
}

public class EventHandlerFactory {
    public <T extends Event> EventHandler<T> getHandler(Class<T> eventType) {
        return ... // implementation?
    }
}
有什么想法我可以做到这一点(甚至类似的事情,如果这是不可能的)

编辑:

具体的事件处理程序可能如下所示:

public class JobCreatedEventHandler implements EventHandler<JobCreatedEvent> 
{
    @Override
    public void handle(JobCreatedEvent event) {
        ...
    }
}
公共类JobCreateDevenHandler实现EventHandler
{
@凌驾
公共无效句柄(JobCreatedEvent事件){
...
}
}

在某一点上,逻辑的某些部分需要认识到类
A
的对象由类
B
的对象处理,类
B
的对象需要有一个名为
handleEvent()
的方法(为了参数),该方法(可能)出现在一个名为(比如)的公共
接口中
EventHandler

无论您是实现类型到实例的映射,还是仅进行hack:

Class<?> c=Class.forName(event.getClass().getName()+"Handler");
Class c=Class.forName(event.getClass().getName()+“Handler”);
或者一长串
if
`else`语句或类似的语句。 必须进行某种映射

我看不出有什么办法可以解决这个问题。

只有休息

  • 在运行时使不安全的映射变得安全,以及
  • 原则上,一般情况下处理事件也是如此

  • 不可避免的问题是,不要在
    Class
    EventHandler
    上保存映射,而是在类型不太安全的
    Class上保存映射,我会选择这样的映射

    public class EventHandlerFactory {
        private HashMap<Class, EventHandler> map = new HashMap<>();
        @SuppressWarnings("unchecked")
        public <T extends Event> EventHandler<T> getHandler(Class<? extends T> eventType) {
            return (EventHandler<T>) map.get(eventType);
        }
    
        public <T extends Event> setHandler(Class<? extends T> eventType, EventHandler<T> handler) {
            map.put(eventType, handler);
        }
    }
    
    公共类EventHandlerFactory{
    private HashMap map=new HashMap();
    @抑制警告(“未选中”)
    
    public EventHandler getHandler(Class使用访问者模式的解决方案(如注释中所要求的):

    注:

    • 如果事件类型集是固定的(因此在子类化方面完全不灵活),则此解决方案可以正常工作
    • 您应该考虑以这样的方式组织类/包/接口:
      handleA
      handleB
      等对
      EventFactory
      的实际客户端类的可见性
    • 默认的
      Event.accept
      方法不是严格要求的;在您的用例中,我认为没有一个合理的
      EventHandlerFactory.defaultHandle
    • 当然,如果需要这种类型的间接寻址,可以使用
      事件.handle
      方法将调用返回到适当的getter方法,例如
      返回factory.getEventHandlerForB

    这个解决方案是完全不灵活的(很大程度上是因为访问者模式是迄今为止最不灵活的模式),但它完成了任务。请注意,不需要按类进行单个强制转换/查找。

    将其中一些“没有帮助”链接起来可能会有所帮助存在的问题。你能给出EventHandler的具体实现的例子吗?很想知道-为什么有“TypeToInstanceMap”没有帮助?如果您有预先存在的事件处理程序类,那么TypeToInstanceMap实际上就是这里的解决方案。如果您手头没有处理程序,那么您可以为它们生成匿名类,并在方法体中进行显式类型检查。如果在编译时不知道事件类型,则不可能使此复杂化类型安全,因为如果类型在编译时未知,编译器无法检查该类型。当您编写“事件类型仅在运行时已知”时,您的确切意思是什么?您的意思是,您有一整套已知的固定事件类型,但您不知道调用
    getEvent()
    时会得到这些固定事件类型中的哪一种特定类型?还是说,通过
    getEvent()检索事件时可能遇到的一整套事件类型
    仅在运行时已知。在后一种情况下,某种类型的“TypeToInstanceMap”是唯一可行的方法。有一个警告:如果在运行时代理
    EventType
    ,此解决方案将不起作用。我认为代理事件没有什么意义,但我在类似的基于映射的解决方案中见过这种情况一两次。为了安全起见,如果找不到处理程序,则应使用超类重复搜索,直到继承层次结构已耗尽或找到匹配项。我还曾经不得不爬上类层次结构以找到处理程序。在这里,根据接口,必须检查所有实现的接口。可能会很可怕。很好,谢谢。这肯定是一种可能性,尽管我希望不必创建
    public void handleA(EventA e)
    出于可扩展性的目的设计方法。理想情况下,我想要一个可以使用DI容器连接的解决方案,而不必为每个新事件类型添加新方法。但这仍然是一个不错的解决方案。好的,谢谢,但是一旦从映射中检索到处理程序,您将如何处理处理命令的有趣部分?
    nterface Event {
    }
    
    interface EventHandler<T extends Event> {
        void handle(T event);
        default void handleGen(Event event) {
            handle((T)event);
        }
    }
    
    class A implements Event {
    }
    
    class B implements Event {
    }
    
    class AHandler implements EventHandler<A> {
    
        @Override
        public void handle(A event) {
            System.out.println("A");
        }
    }
    
    class BHandler implements EventHandler<B> {
    
        @Override
        public void handle(B event) {
            System.out.println("B");
        }
    }
    
    // No type equality required between key and value handler's parameter.
    private Map<Class<? extends Event>, EventHandler<? extends Event>> map = new HashMap<>();
    
    public <T extends Event> void registerHandler(Class<T> eventType,
                EventHandler<T> handler) {
        map.put(eventType, handler);
    }
    
    public void handleEvent(Event event) {
        Class<? extends Event> eventType = event.getClass();
        EventHandler<? extends Event> handler = map.get(eventType);
        if (handler != null) {
            handler.handleGen(eventType.cast(event));
        }
    }   
    
        registerHandler(A.class, new AHandler());
        registerHandler(B.class, new BHandler());
        A a = new A();
        handleEvent(a);
    
            handler.handleGen(eventType.cast(event));
    
    public class EventHandlerFactory {
        private HashMap<Class, EventHandler> map = new HashMap<>();
        @SuppressWarnings("unchecked")
        public <T extends Event> EventHandler<T> getHandler(Class<? extends T> eventType) {
            return (EventHandler<T>) map.get(eventType);
        }
    
        public <T extends Event> setHandler(Class<? extends T> eventType, EventHandler<T> handler) {
            map.put(eventType, handler);
        }
    }
    
    public interface Event {
        ...
        default void accept(EventHandlerFactory factory) {
            factory.defaultHandle(this);
        }
    }
    
    public class EventA implements Event {
        public void accept(EventHandlerFactory factory) {
            factory.handleA(this);
        }
    }
    
    public class EventB implements Event {
        public void accept(EventHandlerFactory factory) {
            factory.handleB(this);
        }
    }
    ...
    
    public class EventHandlerFactory {
        public void handleEvent(Event e) {
            e.accept(this);
        }
    
        public void handleA(EventA e) {
            this.handlerA.handle(e);
        }
    
        public void handleB(EventB e) {
            this.handlerB.handle(e);
        }
        ...
    
        public void defaultHandle(Event e) {
            LOG.error("Handler method not defined for" + e.getClass().getSimpleName());
        }
    }