简体   繁体   中英

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.

I have a class which handles the websocket communication using the Autobahn library weCommunicationManager .

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.

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.

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,

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,

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. 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; same problem.

I have seen lots of examples of enums being used in switch statements, but non quite like I am. 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.

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:

// 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:

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 .

You could add a getFromCode(int code) static method into your MESSAGE_TYPE enum like this:

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:

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( messageType ) {
    case MessageType.PROTOCOL_HANDSHAKE:
        ....
        break;

This is essentially the contract class pattern that Android advocates in other areas (such as ContentResolver, Database, etc).

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. Putting the value in an private final static int would work because that is evaluated on compile time and is therefore a constant expression.

Try something like

private final static int PROTOCOL_HANDSHAKE = 0;

and do this in your switch statement

case PROTOCOL_HANDSHAKE:
//Do something

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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