基于Java中的某个常量动态实例化类

基于Java中的某个常量动态实例化类,java,reflection,instantiation,Java,Reflection,Instantiation,我正在制作一个多人游戏,它大量使用可序列化的事件类通过网络发送消息。我希望能够基于常量重构事件的适当子类 到目前为止,我选择了以下解决方案: public class EventFactory { public static Event getEvent(int eventId, ByteBuffer buf) { switch (eventId){ case Event.ID_A: return EventA.deseriali

我正在制作一个多人游戏,它大量使用可序列化的
事件
类通过网络发送消息。我希望能够基于常量重构
事件
的适当子类

到目前为止,我选择了以下解决方案:

public class EventFactory {

    public static Event getEvent(int eventId, ByteBuffer buf) {
        switch (eventId){
        case Event.ID_A:
            return EventA.deserialise(buf);
        case Event.ID_B:
            return EventB.deserialise(buf);
        case Event.ID_C:
            return EventC.deserialise(buf);
        default:
            // Unknown Event ID
            return null;
        }
    }

}
然而,这让我觉得非常冗长,每次创建新的
事件
类型时都要添加一个新的“case”语句

我知道还有两种方法可以做到这一点,但这两种方法似乎都不好*:

  • 创建常量->事件子类的映射,并使用clazz.newInstance()实例化它们(使用空构造函数),然后使用clazz.initialiase(buf)提供必要的参数
  • 创建常量->事件子类的映射,并使用反射在适当的类中查找和调用正确的方法
  • 有没有比我现在使用的更好的方法?我是否不明智地忽视上述备选方案



    *注意:在这种情况下,更好意味着更简单/更干净,但不会对速度造成太大影响。

    您可以使用
    HashMap
    为eventID获取正确的事件。添加或删除事件将很容易,而且随着代码的增长,与switch case解决方案相比,这很容易维护,而且速度方面也应该比switch case解决方案快

       static
       {
             HashMap<Integer,Event> eventHandlerMap = new HashMap<>();
    
             eventHandlerMap.put(eventId_A, new EventHandlerA());
             eventHandlerMap.put(eventId_B, new EventHandlerB());
             ............
       }
    

    如果你不怕反思,你可以使用:

        private static final Map<Integer, Method> EVENTID_METHOD_MAP = new LinkedHashMap<>();
    
        static {
            try {
                for (Field field : Event.class.getFields())
                    if (field.getName().startsWith("ID_")) {
                        String classSuffix = field.getName().substring(3);
                        Class<?> cls = Class.forName("Event" + classSuffix);
                        Method method = cls.getMethod("deserialize", ByteBuffer.class);
                        EVENTID_METHOD_MAP.put(field.getInt(null), method);
                    }
            } catch (IllegalAccessException|ClassNotFoundException|NoSuchMethodException e) {
                throw new ExceptionInInitializerError(e);
            }
        }
    
        public static Event getEvent(int eventId, ByteBuffer buf)
        throws InvocationTargetException, IllegalAccessException {
            return (Event) EVENTID_METHOD_MAP.get(eventId).invoke(null, buf);
        }
    
    private static final Map EVENTID_METHOD_Map=new LinkedHashMap();
    静止的{
    试一试{
    for(字段:Event.class.getFields())
    if(field.getName().startsWith(“ID_”)){
    字符串classSuffix=field.getName().substring(3);
    Class cls=Class.forName(“事件”+类后缀);
    Method=cls.getMethod(“反序列化”,ByteBuffer.class);
    EVENTID\u METHOD\u MAP.put(field.getInt(null),METHOD);
    }
    }捕获(IllegalAccessException | ClassNotFoundException | NoSuchMethodException){
    抛出新异常初始化错误(e);
    }
    }
    公共静态事件getEvent(int-eventId,ByteBuffer-buf)
    抛出InvocationTargetException,IllegalAccessException{
    return(Event)EVENTID\u METHOD\u MAP.get(EVENTID.invoke)(null,buf);
    }
    
    此解决方案要求
    int ID\u N
    始终映射到
    class event N
    ,其中
    N
    可以是方法
    java.lang.Character.isJavaIdentifierPart(c)
    的所有字符返回
    true
    的任何字符串。另外,
    class EventN
    必须使用一个返回
    事件的
    ByteBuffer
    参数定义一个名为
    反序列化
    的静态方法


    在尝试获取字段值之前,您还可以检查
    字段
    是否是静态的。我现在忘了怎么做。

    有多少种活动?不使用一些标准wire格式(如JSON或XML)有什么特别的原因吗?为什么不实现事件,然后直接写入和读取事件对象?目前大约有20种事件类型,但我将继续添加更多。目前,我正在使用ByteBuffers手动序列化/反序列化事件,正如您在这里看到的,因为它速度快且非常小;我想发送尽可能少的字节。我不能使用Serializable,因为我是以数据包而不是流的形式发送数据的。但我对JSON很好奇——这与发送的速度和数据量相比如何?我喜欢使用映射的想法,但这依赖于提前创建每个事件的实例(我使用的“EventHandler”应该是“Event”),看起来就像占位符一样,并且依赖于空构造函数,使其与我建议的第一个备选方案类似。您可以使用eventid到类名的映射,然后通过反射创建类。但人们普遍认为反射速度很慢。顺便说一句,我正试图推动使用hashmap的想法。我对此持开放态度。这看起来不太吓人,但我不知道它的优点是否大于复杂性。这与switch语句的速度比较如何?
        private static final Map<Integer, Method> EVENTID_METHOD_MAP = new LinkedHashMap<>();
    
        static {
            try {
                for (Field field : Event.class.getFields())
                    if (field.getName().startsWith("ID_")) {
                        String classSuffix = field.getName().substring(3);
                        Class<?> cls = Class.forName("Event" + classSuffix);
                        Method method = cls.getMethod("deserialize", ByteBuffer.class);
                        EVENTID_METHOD_MAP.put(field.getInt(null), method);
                    }
            } catch (IllegalAccessException|ClassNotFoundException|NoSuchMethodException e) {
                throw new ExceptionInInitializerError(e);
            }
        }
    
        public static Event getEvent(int eventId, ByteBuffer buf)
        throws InvocationTargetException, IllegalAccessException {
            return (Event) EVENTID_METHOD_MAP.get(eventId).invoke(null, buf);
        }