为什么我们不允许在java中重写枚举中的hashcode

Ste*_*nZL 2 java enums hashcode

我现在只知道Java中没有Enum的hashCode()实现。它返回的只是 super.hashCode(),而其他不可变类(例如 String)都有自己的 hashCode() 实现。这使得 Enum 在不同 JVM 中使用时不安全。我认为ordinal()非常适合计算Enum的hashCode(),但是Enum中的hashCode()被定义为final,我无法覆盖它。我能想到的唯一解决方案是创建一个类似于 Enum 的全新类。有什么建议吗?


我想实现这样的目标:

enum Fruit {
        APPLE, POMEGRANATE, KIWI;
        
        @override
        public int hashCode() {
            return Objects.hash(super.ordinal());
        }
        @override
        public boolean equals(Object obj) {
            if (this == obj)
                return true;
            if (obj == null)
                return false;
            if (getClass() != obj.getClass())
                return false;
            }
            Fruit other = (Fruit)obj;
            return super.ordinal() == other.ordinal();
        }
}
Run Code Online (Sandbox Code Playgroud)

有什么解决办法吗?或者我想了解为什么这个解决方案不好,因此在java中不允许。

Bas*_*que 5

你说:

\n
\n

这使得 Enum 在不同 JVM 中使用时不安全。

\n
\n

正如shmosel 所评论的那样,您永远不能依赖hashCode任何对象在 JVM 之间保持一致。

\n

所承诺的合同Object##hashCode明确指出,您不能依赖hashCodeJVM 的调用之间的相同性。引用Javadoc:

\n
\n

从应用程序的一次执行到同一应用程序的另一次执行,该整数不需要保持一致。

\n
\n

你说:

\n
\n

我认为 ordinal() 非常适合计算 Enum 的 hashCode()

\n
\n

不,我想不是。您可以定义应用程序版本之间枚举元素的顺序。您甚至可以添加或删除元素。因此,我们通常不应该依赖应用程序版本之间的序号。

\n

你说:

\n
\n

我能想到的唯一解决方案是创建一个类似于 Enum 的全新类。

\n
\n

我相信你会遇到麻烦。

\n

你说:

\n
\n

有什么建议吗?

\n
\n

是的。指定一个键。

\n

如果您想标识由 subclass\xe2\x80\x99 对象表示的实体的值Enum,我建议您向枚举类添加一个成员字段以唯一标识每个实例。

\n

自然键

\n

您分配的值取决于您的问题域。在特定行业或公司中,可能存在已知的标识符。例如,在处理水果时,也许可以指定一个拉丁科学植物学名称。

\n

在关系数据库理论中,这个名称是自然键

\n
enum Fruit {\n    APPLE ( "Malus domestica" ) , \n    POMEGRANATE ( "Punica granatum" ) , \n    KIWI ( "Actinidia deliciosa" );\n    \n    final String botanicalName ;\n    \n    Fruit ( String botanicalName ) \n    {\n        this.botanicalName = botanicalName ;\n    }\n}\n
Run Code Online (Sandbox Code Playgroud)\n

用法:

\n
for( Fruit fruit : Fruit.values() )\n{\n    System.out.println( fruit + " = " + fruit.botanicalName ) ;\n}\n
Run Code Online (Sandbox Code Playgroud)\n

运行时。

\n
APPLE = Malus domestica\nPOMEGRANATE = Punica granatum\nKIWI = Actinidia deliciosa\n
Run Code Online (Sandbox Code Playgroud)\n

请参阅在 Ideone.com 上实时运行的代码

\n

代理键(任意标识符)

\n

如果您的业务域中不存在此类标识符,则分配任意永久标识符。

\n

如果您不知道,请使用通用唯一标识符 (UUID)。看UUID班级。

\n

在关系数据库理论中,我们称之为代理键

\n
enum Fruit {\n    APPLE ( UUID.fromString( "9307e05e-b337-41e8-acec-a00645b00878" ) ) , \n    POMEGRANATE ( UUID.fromString( "6f67df08-b400-49af-94ed-e068ee58412f" ) ) , \n    KIWI ( UUID.fromString( "028c9e8a-9d25-48a1-b150-b892ea26807f" ) ) ;\n    \n    final UUID id ;\n    \n    Fruit ( UUID id ) \n    {\n        this.id = id ;\n    }\n}\n
Run Code Online (Sandbox Code Playgroud)\n

用法:

\n
for( Fruit fruit : Fruit.values() )\n{\n    System.out.println( fruit + " \xe2\x9e\x94 " + fruit.id ) ;\n}\n
Run Code Online (Sandbox Code Playgroud)\n

运行时。

\n
APPLE \xe2\x9e\x94 9307e05e-b337-41e8-acec-a00645b00878\nPOMEGRANATE \xe2\x9e\x94 6f67df08-b400-49af-94ed-e068ee58412f\nKIWI \xe2\x9e\x94 028c9e8a-9d25-48a1-b150-b892ea26807f\n
Run Code Online (Sandbox Code Playgroud)\n
\n

顺便说一句,从技术上来说,说 \xe2\x80\x9c 没有实现 \xe2\x80\x9d 是不正确hashCode()Enum。继承的实现就是它的实现。这种观点是OOP的基础。

\n