为什么Java不允许在枚举类型中重写equals(Object)方法?

43

我注意到以下代码片段...

@Override
public boolean equals(Object otherObject) {
    ...
}

枚举类型不允许重写equals(Object x)方法,因为该方法在Enum中被定义为final。为什么会这样呢?

我无法想象出任何需要重写equals(Object)方法的枚举用例。只是好奇这种行为背后的原因。


我的使用场景是当我通过JSON接收到一个字符串值,并希望查看它是否与我的枚举中的现有值匹配。虽然在我的情况下,我有一个枚举构造函数,它接收一个字符串值,我将其保存在一个私有变量中,所以归根结底,我想比较字符串吧。但我更喜欢使用 MY_ENUM.VALUE.equals("hello") 而不是 MY_ENUM.VALUE.toString().equals("hello")。希望这样讲清楚了。 - zundi
6个回答

47

带有 return this == other 的任何内容都会违反最小惊讶原则,并且不符合直觉。如果两个枚举常量是同一个对象,则期望它们相等,否则就会出现错误。

同样的推理适用于 hashCode()clone()compareTo(Object)name()ordinal()getDeclaringClass()


JLS并没有鼓励将其设为final,但在枚举这里的上下文中提到了equals。代码片段:

Enum中的equals方法是一个final方法,仅在其参数上调用super.equals并返回结果,因此执行标识比较。


3

对于一个enum类型的实例(值)相等已经有了强烈的直觉概念。允许重载equals方法将导致这种概念被违反,从而导致意外行为、错误等问题。


2
我赞同大多数观点。如果禁止覆盖Enum::equals,我认为这不是为了遵循最小惊讶原则,而是为了不破坏Java。
在JVM中,以及一些旧的类(如EnumMap)中,枚举标识可能会内部编码为int。例如,如果我们允许覆盖Enum::equals,则EnumMap<K,V>将违反Map的契约。

2
有时候我们需要处理不符合Java命名规范的数据。能够像这样做事情会很好: ```java ``` 请注意,此处没有给出代码示例。
public enum Channel
{
    CallCenter("Call Center"),
    BankInternal("Bank Internal"),
    Branch("Branch");

    private final String value;

    Channel(String value)
    {
        this.value = value;
    }

    @Override
    public String toString()
    {
        return value;
    }

    public static Channel valueOf(String value)
    {
        for (Channel c : Channel.values())
            if (c.value.equals(value))
                return c;
        return null;
    }

    @Override
    public boolean equals(Object other) 
    {
        if (other instanceof String)
            other = Channel.valueOf((String)other);
        return super.equals(other);
    }
}

"String"类需要进行修改以适应...
public boolean equals (Object object) {
    if (object == this) return true;
    if (object instanceof Enum) 
        object = object.toString();
    if (object instanceof String) {
        String s = (String)object;
        // There was a time hole between first read of s.hashCode and second read
        //  if another thread does hashcode computing for incoming string object
        if (count != s.count ||
            (hashCode != 0 && s.hashCode != 0 && hashCode != s.hashCode))
                return false;
        return regionMatches(0, s, 0, count);
    }
    return false;
}

1
一个问题,为什么你不让客户端代码先执行valueOf字符串,然后再比较结果的枚举?这样我们仍然在比较枚举,而且我们不需要围绕equals和hasCodes来操作。 - Ashwin Prabhu

1

正是因为Java设计者无法想象重写Enum.equals(Object)的任何可行用例,所以该方法被声明为final - 以使这样的重写成为不可能。


0

我必须承认,在枚举类中覆盖equals()是我最不想做的事情。

我认为枚举类中equals()被设置为final的原因是Java鼓励使用==进行枚举比较,而在枚举类中实现equals()只是简单地使用它,所以防止覆盖equals()可以避免==equals()的行为不同,这是其他开发人员不期望看到的。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接