Java 如何创建类似于“的常量分组”;“动态枚举”;在爪哇?

Java 如何创建类似于“的常量分组”;“动态枚举”;在爪哇?,java,reflection,enums,Java,Reflection,Enums,在Java中声明来自多个库的常量分组的选项有哪些 例如,我有一个网络消息协议。该协议有一些标准消息,并允许创建自定义消息。每封邮件都有一个唯一的ID号。我真的很想在我的应用程序中将所有这些唯一ID合并到一个枚举中。类似于 public enum MessageType { //Standard messages in one library FooMsg(1), BarMsg(2), //Custom messages in another library

在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;
    }
}
然后,我可以创建消息类型的通用方法

public void doStuff(MessageType msgType, MyMessageObject data)
{
   switch(msgType)
   {
      case FooMsg: //Do stuff
       break;
    //Other stuff
   }
}
但是,枚举要求我在编译时知道所有消息类型,但消息分布在多个库中。其中一些库是可选加载的,因此如果它们没有加载,我不想将它们包括在枚举中


有没有一种方法可以在运行时定义一个类似于枚举的“常量集合”

您不能使用
枚举
,但是有没有任何原因表明简单的
映射
不能满足您的需要?或者,因为您有一些其他实用程序方法,所以是一个由映射支持的类?您可以只为已加载的库添加那些消息,如果在应用程序的加载阶段和运行阶段之间有明确的划分,您甚至可以使用构建器模式并将映射冻结为不可变映射,这样一旦应用程序运行,就无法对消息进行进一步的更改

(这可以通过
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);
    }
}

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

也许可以使用某种类型的
地图
?Key=name,value=constant value?我不担心备份数据结构。是的,我保证不会在名字或消息ID号上发生冲突。我的目标是保留保持编译保护的能力,这样我就可以执行“void doStuff(MessageType msgType)”而不是“void doStuff(int msgType)”之类的操作。好吧,您可以使用映射支持的值类来实现这一点。value类将是您用作参数的类型,映射将通过其
valueOf
方法使用,该方法将数字解析为value类的实例。我给你举个例子来说明我的意思。不需要举个例子,我知道你的意思。谢谢
@Override
public String toString() {
    return "message:" + name + "<" + number + ">";
}