简体   繁体   English

Java Switch Statememt - 使用抽象类的公共枚举所需的常量表达式

[英]Java Switch Statememt - Constant Expression Required Using Public Enum from Abstract Class

I have written code working with a protocol I have design on top of websockets for a project creating an Android App. 我编写的代码使用了我在webockets上设计的协议,用于创建Android应用程序的项目。

I have a class which handles the websocket communication using the Autobahn library weCommunicationManager . 我有一个使用Autobahn库weCommunicationManager处理websocket通信的类。

I have a number of variables and tags which are needed to process the received JSON messages coming over websockets to the app from my server. 我有许多变量和标签,用于处理从我的服务器通过websockets到应用程序的收到的JSON消息。

To keep things separate and in one place, I have created an abstract class ExICSProtocol which holds the tags as public static members so they can be referenced from wherever necessary. 为了使事物分离并在一个地方,我创建了一个抽象类ExICSProtocol ,它将标记保存为公共静态成员,以便可以在必要时引用它们。

Within the received messages is an integer value for the message type which I need to be able to switch on to define how to process that particular received message. 在收到的消息中是消息类型的整数值,我需要能够打开它来定义如何处理该特定的接收消息。

For this I have implemented a public enum in the ExICSProtocol class as follows, 为此,我在ExICSProtocol类中实现了一个公共枚举,如下所示,

public static enum MESSAGE_TYPE{
    PROTOCOL_HANDSHAKE(0),
    USER_CONNECTED(1),
    USER_DISCONNECTED(2),
    SYSTEM_STATE(3),
    CHANGE_ROOM(4),
    EXAM_START(5),
    EXAM_PAUSE(6),
    EXAM_STOP(7),
    EXAM_XTIME(8),
    SEND_MESSAGE(9),
    SUCCESS(69),
    FAILURE(-1),
    TERMINATE_CONNECTION(-2);

    private int code;

    MESSAGE_TYPE(int code){
        this.code = code;
    }

    public int getCode(){
        return this.code;
    }
}

I am trying to use this in the wsCommunicationManager code as follows, 我试图在wsCommunicationManager代码中使用它,如下所示,

private static void handleMessage(String message){
    try{
        JSONObject messageObject = new JSONObject(message);
        JSONObject messageHeader = messageObject.getJSONObject(ExICSProtocol.TAG_HEADER);
        JSONObject messagePayload = messageObject.getJSONObject(ExICSProtocol.TAG_PAYLOAD);

        int messageType = messageHeader.getInt(ExICSProtocol.TAG_MESSAGE_TYPE);


        switch(messageType){
            case ExICSProtocol.MESSAGE_TYPE.PROTOCOL_HANDSHAKE.getCode():
                //DO SOMETHING HERE
                break;

            ...

            default:
                throw new ExICSException("Unknown Message Type Received");
                break;
        }
    }catch (JSONException e){
        Log.e(TAG, "Failed to parse received message " + message, e);
    }catch (ExICSException e){
        Log.e(TAG, "Excaption Handling Message Occurred", e);
    }
}

I am getting errors flagged up under ExICSProtocol.MESSAGE_TYPE.PROTOCOL_HANDSHAKE.getCode(): saying that a constant expression is required. 我在ExICSProtocol.MESSAGE_TYPE.PROTOCOL_HANDSHAKE.getCode():下标记错误ExICSProtocol.MESSAGE_TYPE.PROTOCOL_HANDSHAKE.getCode():说需要一个常量表达式。 This however SHOULD be constant? 然而,这应该是不变的?

I have tried a few different things, moving the enum to the class where it is being used; 我尝试了一些不同的东西,将枚举移动到正在使用它的类中; same problem. 同样的问题。 Making the private int held by the enum public so it can directly be accessed; 使enum公共持有私有int,以便可以直接访问; same problem. 同样的问题。

I have seen lots of examples of enums being used in switch statements, but non quite like I am. 我已经看到很多在switch语句中使用枚举的例子,但非常像我。 Am I missing something in my declaration or initialization, or am trying to do something that won't work. 我在声明或初始化中遗漏了什么,或者我正在尝试做一些无效的事情。

I know there are relatively simple workarounds such as just defining the numerical type codes as public static final ints, but I wanted the type codes to be held together under MESSAGE_TYPE if possible. 我知道有相对简单的解决方法,例如将数字类型代码定义为公共静态最终整数,但我希望在可能的情况下将类型代码保存在MESSAGE_TYPE下。

As you've seen, calling a method doesn't count as a constant expression . 正如您所见,调用方法不算作常量表达式

The cleaner solution (IMO) is to have a static method within your enum: 更清洁的解决方案(IMO)是在枚举中使用静态方法:

// Enum renamed to comply with conventions
public enum MessageType {

    private static final Map<Integer, MessageType> codeMap = new HashMap<>();
    // Can't do this in the constructor, as the codeMap variable won't have been
    // initialized
    static {
        for (MessageType type : MessageType.values()) {
            codeMap.put(type.getCode(), type);
        }
    }

    public static MessageType fromCode(int code) {
        return codeMap.get(code);
    }
}

Then in your wsCommunicationManager code: 然后在你的wsCommunicationManager代码中:

int messageCode = messageHeader.getInt(ExICSProtocol.TAG_MESSAGE_TYPE);
MessageType messageType = MessageType.fromCode(messageCode);
if (messageType == null) {
    // Unknown message code. Do whatever...
}

switch(messageType) {
    case MessageType.PROTOCOL_HANDSHAKE:
        ...
}

Basically, go from the "integer-oriented" to the "object-oriented" world as early as you can. 基本上,尽早从“面向整数”转向“面向对象”的世界。 This is a bit like parsing numeric and date-based user input early instead of passing strings around - it's just that in your case the surrogate representation is an int . 这有点像早期解析基于数字和日期的用户输入而不是传递字符串 - 在你的情况下,代理表示是一个int

You could add a getFromCode(int code) static method into your MESSAGE_TYPE enum like this: 你可以在你的MESSAGE_TYPE枚举中添加一个getFromCode(int code)静态方法,如下所示:

public static MESSAGE_TYPE getFromCode(int code) {
    for(MESSAGE_TYPE mt : MESSAGE_TYPE.values())
        if(mt.code == code)
            return mt;

    throw new IllegalArgumentException();
}

And then in your handleMessage: 然后在你的handleMessage中:

MESSAGE_TYPE messageType = MESSAGE_TYPE.getByCode(messageHeader.getInt(ExICSProtocol.TAG_MESSAGE_TYPE));


switch(messageType){
    case PROTOCOL_HANDSHAKE:
        //handle handshake...

Unless your enum does something else that you didn't show us above, this is not a very good use of enums. 除非你的枚举做了一些你没有告诉我们的内容,否则这不是一个很好用的枚举。 For this particular case, a static class might be more useful (and readable): 对于这种特殊情况,静态类可能更有用(和可读):

public class MessageType {
    public static final int PROTOCOL_HANDSHAKE = 0;
    public static final int USER_CONNECTED = 1;
    ....
}

Then in your switch statement, you can use 然后在你的switch语句中,你可以使用

switch( messageType ) {
    case MessageType.PROTOCOL_HANDSHAKE:
        ....
        break;

This is essentially the contract class pattern that Android advocates in other areas (such as ContentResolver, Database, etc). 这基本上是Android在其他领域(例如ContentResolver,Database等)提倡的合同类模式。

You are using a function which is evaluated on runtime. 您正在使用在运行时评估的函数。 Putting the same value in an int would also not work, because it is also evaluated at runtime. 将相同的值放在int中也不起作用,因为它也在运行时进行评估。 Putting the value in an private final static int would work because that is evaluated on compile time and is therefore a constant expression. 将值放在private final static int会起作用,因为它是在编译时计算的,因此是一个常量表达式。

Try something like 尝试类似的东西

private final static int PROTOCOL_HANDSHAKE = 0;

and do this in your switch statement 并在您的switch语句中执行此操作

case PROTOCOL_HANDSHAKE:
//Do something

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM