如何对具有多个值的 Enum 进行编码和解码

LK1*_*905 1 java enums

我有 2 个类,一个具有 int 值和一个 String 值的枚举,以及一​​个常规类。

\n

枚举类:

\n
public enum LettersEnum{\n\n    A(0, \xe2\x80\x9cLetter A\xe2\x80\x9d),\n    B(1, \xe2\x80\x9cLetter B\xe2\x80\x9d),\n    C(2, \xe2\x80\x9cLetter C\xe2\x80\x9d);\n\n    private final id;\n    private final letter;\n\n    LettersEnum(int id, String letter) {\n        this.id = id;\n        this.letter = letter;\n    }\n\n    public int getId() {\n        return this.id;\n    }\n\n    public String getLetter() {\n        return this.letter;\n    }\n}\n
Run Code Online (Sandbox Code Playgroud)\n

常规班:

\n
public class LettersClass {\n    \n    private LettersEnum letter = LettersEnum.A;\n\n    public void setLetter(LettersEnum letter) {\n        this.letter = letter;\n    }\n\n    public LettersEnum getLetter() {\n        return this.letter;\n    }\n}\n
Run Code Online (Sandbox Code Playgroud)\n

我有一个具有客户端和服务器端的视频游戏。两端A都是默认字母。如果我在客户端更改AB(或反之亦然),它不会更改服务器端的字母,除非我运行一个数据包,将整个枚举编码为单个数据类型,并在服务器端解码回枚举。

\n

问题是,我该怎么做?我猜测toString(),但是我如何将每个值转换回正确的数据类型?

\n

rzw*_*oot 5

这称为序列化:将任何随机信息单元(在本例中为枚举值之一)转换为可序列化数据(通常为字节,但根据底层传输/存储机制,有时必须具有字符)。

\n

有很多方法可以完成这项工作。特别是对于枚举,一种特别明显的方法是序列化序数或名称。或者,您可以手动管理自己的序列化“密钥”。

\n

序数词

\n

“序数”只是枚举定义中的索引位置。在您的情况下, 的序数A为 0 只是因为它A是您列出的第一个。(并不是因为该id字段包含 0,这只是巧合)。

\n

通过序号进行序列化的一个主要问题是,如果您重新排序枚举值或在除末尾之外的任何位置插入新值,一切都会中断。如果您这样做,您绝对应该在代码中添加一条注释,表明永远不能弄乱现有值,您只能在列表末尾添加值。

\n

序数的优点是发送和接收都很简单,而且效率很高:它只是一个 int;32 位。

\n

将枚举值转换为序数

\n
LettersEnum letter = LettersEnum.A;\nint o = letter.ordinal();\nassert o == 0;\n
Run Code Online (Sandbox Code Playgroud)\n

将序数转换回枚举

\n
int o = 0;\nLettersEnum letter = LettersEnum.values()[o];\nassert letter == LettersEnum.A;\n
Run Code Online (Sandbox Code Playgroud)\n

values()方法很“神奇”,所有枚举都只有这些,您不需要编写它。

\n

名称

\n

您还可以选择通过枚举名称进行序列化;在本例中,A例如。这确实允许您在代码中重新组织枚举,但如果这样做,您将无法重命名任何枚举。

\n

第二个问题是序列化字符串稍微复杂一些;为了“安全”地做到这一点,您需要首先将字符串转换为字节(因为绝大多数传输/存储机制都以字节为单位),并且您需要确保在执行此操作时使用 UTF-8 编码,否则您例如,当您开始添加字母 \xc3\x9f,这是一个有效的 java 标识符(enum Foo { A, B, \xc3\x9f; }是有效的 java!)时,就会遇到问题。

\n

将枚举值转换为名称,然后将名称转换为字节

\n
LettersEnum letter = LettersEnum.A;\nString name = letter.name();\nassert name.equals("A");\nbyte[] data = name.getBytes(StandardCharsets.UTF_8);\nassert data.length == 1 && data[0] == 'A';\n
Run Code Online (Sandbox Code Playgroud)\n

将字节转换为字符串,然后转换为枚举

\n
byte[] data = new byte[] {'A'};\nString name = new String(data, StandardCharsets.UTF_8);\nLettersEnum v = LettersEnum.valueOf(name);\nassert v == LettersEnum.A;\n
Run Code Online (Sandbox Code Playgroud)\n

和方法都已经存在,您不必编写它们name()valueOf()

\n

您自己的身份证

\n

上述两种策略都会默默地“链接”方面,我们 java 程序员通常认为这些方面不会对程序的运行时行为产生任何影响(为基于序数的序列化编写字段的顺序,以及值基于名称的名称),因此您或开发团队的其他成员可能会重命名事物,但没有意识到这意味着旧代码现在“保存”或“发送”的任何内容都无法再由新代码检索/接收。一种简单的方法是使用您自己的 ID。如果您只是对现有概念进行建模(例如,某些第三方 API 已经定义了一堆具有整数值的命令,但没有人喜欢),那么这也可能是明智的,这样会.sendCmd(182, ..)更好.sendCmd(Command.LIST_USERS)

\n

你已经拥有了这个——一个id字段。

\n

无论在哪个方向,都没有对此的内置支持;你必须自己写。但这并不是特别复杂:

\n
public enum LettersEnum{\n\n    A(0, \xe2\x80\x9cA\xe2\x80\x9d),\n    B(1, \xe2\x80\x9cB\xe2\x80\x9d),\n    C(2, \xe2\x80\x9cC\xe2\x80\x9d);\n\n    private final id;\n    private final letter;\n\n    LettersEnum(int id, String letter) {\n        this.id = id;\n        this.letter = letter;\n    }\n\n    public int getId() {\n        return this.id;\n    }\n\n    public String getLetter() {\n        return this.letter;\n    }\n\n    private static final Map<Integer, LettersEnum> ID_TO_LETTER;\n    static {\n        var map = new HashMap<Integer, LettersEnum>();\n        for (LettersEnum letter : values()) map.put(letter.getId(), letter);\n        ID_TO_LETTER = Collections.unmodifiableMap(map);\n    }\n\n    public static LettersEnum ofId(int id) {\n        LettersEnum v = ID_TO_LETTER.get(id);\n        if (v != null) return v;\n        throw new IllegalArgumentException(id + " is not a known LettersEnum id");\n    }\n}\n
Run Code Online (Sandbox Code Playgroud)\n

并使用:

\n
LettersEnum letter = LettersEnum.A;\nint i = letter.getId();\n// store i someplace...\n
Run Code Online (Sandbox Code Playgroud)\n
// retrieve o someplace...\nint o = 0;\nLettersEnum letter = LettersEnum.ofId(o);\nassert letter == LettersEnum.A;\n
Run Code Online (Sandbox Code Playgroud)\n