Java Switch Statememt – Constant Expression Required Using Public Enum from Abstract Class

androidenumsjavaswitch statement

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.

Best Answer

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.