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

时间:2017-09-19 12:26:09

标签: java generics

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

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

在代码中,这是我想要实现的目标的说明:

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?
    }
}

然后能够以类型安全的方式使用事件,如下所示:

Event event = getEvent("some json representation");
factory.getHandler(event.getClass()).handle(event);

任何想法如何实现这一点(如果不可能,甚至是类似的东西)?

编辑:

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

public class JobCreatedEventHandler implements EventHandler<JobCreatedEvent> 
{
    @Override
    public void handle(JobCreatedEvent event) {
        ...
    }
}

4 个答案:

答案 0 :(得分:2)

使用访客模式的解决方案(根据评论中的要求):

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

注意:

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

这个解决方案完全没有灵活性(几乎因为访问者模式是有史以来发明的最不灵活的模式),但它完成了工作。请注意,不需要按类进行单个强制转换/查找。

答案 1 :(得分:1)

只有

  1. 在运行时安全地使用不安全的地图,
  2. 原则上处理事件的方式相同。
  3. 不可避免的问题是不要将地图放在Class<T>EventHandler<T>上,而是放在较不安全的Class<? extends Event>上 - 不能用成对类型安全映射。

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

    解决方案在handleGen中:

            handler.handleGen(eventType.cast(event));
    

    抽象基类可以包含一个包私有泛型方法handleGen  委托真正的句柄方法。由于注册方法,这在运行时是安全的。

答案 2 :(得分:0)

在某些时候,逻辑的某些部分需要认识到类A的对象由类B的对象处理,而类B的对象需要有一个方法(为了论证而称为handleEvent(),它(可能)出现在一个叫interface的{​​{1}}中。

您是实现类型到实例的地图还是只是黑客攻击:

EventHandler

Class<?> c=Class.forName(event.getClass().getName()+"Handler"); `else`语句的长链或类似的东西。 必须进行某种映射。

我没有看到解决方法。

答案 3 :(得分:0)

我会选择这样的东西。

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

地图不能强制执行类型安全,因此它由setter方法强制执行。事件类型只能映射到该类或其超类之一的处理程序。