如何在Java中创建类似于"动态枚举"的常量分组?

Tan*_*ir1 2 java reflection enums

在Java中声明来自多个库的常量分组有哪些选择?

例如,我有一个网络消息协议.该协议具有一些标准消息,并允许创建自定义消息.每条消息都有一个唯一的ID号.我真的想将所有这些唯一ID组合到我的应用程序中的枚举中.就像是...

public enum MessageType
{
    //Standard messages in one library
    FooMsg(1),
    BarMsg(2),

    //Custom messages in another library
    MyCustomMessage(100),
    MyOthercustomMessage(101);

    private long msgNumber;
    public long getMessageNumber(long msgNumber) { return msgNumber;}

    public static MessageType fromMessageNumber(long messageNumber)
    {
    //Reverse lookup code here...
    }

    //Few more utilities that don't matter for this question

    private MessageType(long msgNumber)
    {
        this.msgNumber = msgNumber;
    }
}
Run Code Online (Sandbox Code Playgroud)

然后我可以创建一个消息类型的通用方法.

public void doStuff(MessageType msgType, MyMessageObject data)
{
   switch(msgType)
   {
      case FooMsg: //Do stuff
       break;
    //Other stuff
   }
}
Run Code Online (Sandbox Code Playgroud)

但是,枚举要求我在编译时知道所有消息类型,但消息分布在多个库中.其中一些库是可选的加载,因此如果它们未加载,我不想将它们包含在枚举中.

有没有办法在运行时定义一个"常量集合",其功能类似于枚举?

Dav*_*rad 5

你不能用a来做enum,但是有什么理由说简单Map不能满足你的目的吗?或者,既然你有一些其他的实用方法,一个由Map支持的类?您只能为已加载的库添加这些消息,并且如果应用程序的加载阶段和运行阶段之间有明确的描述,您甚至可以使用构建器模式并将Map冻结为不可变映射,以便一次应用程序正在运行,无法对消息进行进一步更改.

(这可以使用Collections.unmodifiableMap()Guava中的任何一个或一个不可变映射类型来完成.)

我不清楚的一件事是,您是否可以保证不同库使用的消息不会发生冲突.这可能是一个问题.

例如:

import java.util.HashMap;
import java.util.Map;

public class MessageType {
    private final static Map<Long, MessageType> messageTypes = new HashMap<>();

    // it isn't clear how the libraries would provide information on
    // the message types they support; suppose that each library has a
    // MessageInfo that implements Map<Long, String> with all its types:
    public static void loadLibrary(MessageInfo messageInfo) {
        // populate the map with each message type it supports
        for (Map.Entry<Long, String> entry : messageInfo.entrySet()) {
            MessageType messageType =
                new MessageType(entry.getKey(), entry.getValue());
            messageTypes.put(entry.getKey(), messageType);
        }
    }

    // A MessageType has a name and a number
    private final long number;
    private final String name;

    private MessageType(long number, String name) {
        this.number = number;
        this.name = name;
    }

    public long getMessageNumber() {
        return number;
    }

    public String getMessageName() {
        return name;
    }

    public static MessageType fromMessageNumber(long number) {
        if (!messageTypes.containsKey(number)) {
            throw new IllegalArgumentException("Unknown message: " + number);
        }
        return messageTypes.get(number);
    }
}
Run Code Online (Sandbox Code Playgroud)

您还可以添加检查,以确保没有定义重复的消息类型,有消息类型null名称或号码不被创建,并添加hashCode(),equals(Object)toString()那个在名称和数量取决于方法.例如,

@Override
public String toString() {
    return "message:" + name + "<" + number + ">";
}
Run Code Online (Sandbox Code Playgroud)

或者您希望显示消息以进行调试.请注意,因为只为每种消息类型创建了一个MessageType对象,所以将它们与它们进行比较应该是安全的==,尽管您没有获得从Java enum类型获得的强大保护.(仍然可以MessageType通过反射创建重复的对象,或者,如果你Serializable通过序列化和反序列化来创建重复的对象.)(这种方法,其中每个不同的值只创建一个实例,称为Flyweight设计模式,你可能知道.)