如何在 Java 中创建类似于 "dynamic enumerations" 的常量分组?



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

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

是否有一种方法可以在运行时定义一个类似于枚举的"常量集合"?

您不能使用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);
    }
}

您还可以添加检查以确保没有定义重复的消息类型,没有创建具有null名称或数字的消息类型,并添加依赖于名称和数字的hashCode()equals(Object)toString()方法。例如,

@Override
public String toString() {
    return "message:" + name + "<" + number + ">";
}

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

相关内容

  • 没有找到相关文章