【问题标题】:Java Switch Statememt - Constant Expression Required Using Public Enum from Abstract ClassJava Switch Statememt - 使用抽象类中的公共枚举需要常量表达式
【发布时间】:2014-05-22 13:42:21
【问题描述】:

我已经编写了使用我在 websockets 之上为创建 Android 应用程序的项目设计的协议的代码。

我有一个使用 Autobahn 库 weCommunicationManager 处理 websocket 通信的类。

我有许多变量和标签,用于处理从我的服务器通过 websockets 到应用程序的接收到的 JSON 消息。

为了将事物分开并放在一个地方,我创建了一个抽象类ExICSProtocol,它将标签作为公共静态成员保存,以便可以在任何需要的地方引用它们。

在接收到的消息中是消息类型的整数值,我需要能够打开它来定义如何处理接收到的特定消息。

为此,我在 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;
    }
}

我正在尝试在 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);
    }
}

我在ExICSProtocol.MESSAGE_TYPE.PROTOCOL_HANDSHAKE.getCode(): 下收到错误标记,说需要一个常量表达式。然而,这应该是不变的吗?

我尝试了一些不同的方法,将枚举移动到使用它的类;同样的问题。使 enum 持有的私有 int 公开,以便可以直接访问;同样的问题。

我见过很多在 switch 语句中使用枚举的例子,但和我不太一样。我是否在声明或初始化中遗漏了某些内容,或者我正在尝试做一些不起作用的事情。

我知道有一些相对简单的解决方法,例如将数字类型代码定义为公共静态最终整数,但如果可能的话,我希望将类型代码放在 MESSAGE_TYPE 下。

【问题讨论】:

  • 不是常量,是方法调用...
  • 我还尝试将 int“代码”本身公开并直接访问它,删除方法调用,但这会产生相同的错误,正如我在问题中所说的那样,例如 ExICSProtocol.MESSAGE_TYPE.PROTOCOL_HANDSHAKE.code
  • 嗯,它仍然不是一个常数;这是一个字段访问。只有枚举值是(事实上,它们是类,在运行时与它们的封闭类同时加载)。
  • 请记住,不鼓励在 Android 平台上使用枚举。

标签: java android enums switch-statement


【解决方案1】:

如您所见,调用方法不算作constant expression

更清洁的解决方案 (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);
    }
}

然后在您的 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:
        ...
}

基本上,尽早从“面向整数”到“面向对象”的世界。这有点像提前解析数字和基于日期的用户输入,而不是传递字符串 - 只是在您的情况下,代理表示是 int

【讨论】:

  • 感谢您的深入回答
  • 就您而言,与 323go 相比,这种或 Icewind 的方式有什么好处吗?
  • @o0rebelious0o:这不需要创建一个包含所有值的新数组(通过values() 方法)并在每次调用时线性搜索它们。诚然,它确实将int 装箱 - 但对于实际上不会分配新对象的低值。这绝对比 323go 只拥有一堆常量整数的方法更干净(IMO)。它更加面向对象 - 随着时间的推移,您可能会发现您能够向 MessageType 添加更多功能。
【解决方案2】:

您可以像这样将 getFromCode(int code) 静态方法添加到您的 MESSAGE_TYPE 枚举中:

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

    throw new IllegalArgumentException();
}

然后在你的handleMessage中:

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


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

【讨论】:

  • 刚刚将其添加到代码中,并且工作起来就像一个魅力,伟大的工作,看起来也更好(y)
【解决方案3】:

除非您的枚举执行了您在上面没有向我们展示的其他操作,否则这不是枚举的一个很好的用途。对于这种特殊情况,静态类可能更有用(且可读性强):

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

然后在你的switch语句中,你可以使用

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

这本质上是Android在其他领域(如ContentResolver、Database等)提倡的契约类模式。

【讨论】:

  • 我实际上已经尝试过静态类,但我将它作为内部类添加到 ExICS 协议中,因此无法正常工作。也许将它作为一个单独的类来实现会更好
  • 您可以将其设为公共或静态类,然后在需要时在其他地方访问它。我假设在这种情况下,性能是一个问题,这将使该解决方案优于枚举和 HashMap 杂技。
  • 我认为你可能是对的,将常量烘焙到字节码中肯定比不得不调用从数据结构中获取值、冒着 npe 等风险
【解决方案4】:

您正在使用在运行时评估的函数。将相同的值放入 int 也是行不通的,因为它也在运行时被评估。 将值放在 private final static int 中会起作用,因为这是在编译时评估的,因此是一个常量表达式。

试试类似的东西

private final static int PROTOCOL_HANDSHAKE = 0;

并在您的 switch 语句中执行此操作

case PROTOCOL_HANDSHAKE:
//Do something

【讨论】:

  • 我知道这会起作用,但我一直在故意避免这种情况,因此我可以将各种消息类型保留在 MESSAGE_TYPES 保护伞下,从而尝试使枚举正常工作。这是我的后备计划
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-04-19
  • 2023-01-16
  • 1970-01-01
  • 2013-05-02
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多