43

I've noticed that the following snippet...

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

...is not allowed for an Enum, since the method equals(Object x) is defined as final in Enum. Why is this so?

I cannot think of any use case which would require overriding equals(Object) for Enum. I'm just curious to know the reasoning behind this behavior.

aioobe
  • 413,195
  • 112
  • 811
  • 826
Ashwin Prabhu
  • 9,285
  • 5
  • 49
  • 82
  • My use case is when I receive a string value via JSON and want to see if it matches an existing value in my enum. Although in my case I have a enum constructor which receives a string value which I keep in a private variable, so at the end of the day I'm comparing strings I guess. But I would prefer to do `MY_ENUM.VALUE.equals("hello")` then `MY_ENUM.VALUE.toString().equals("hello")` Hope this makes sense. – zundi Sep 05 '18 at 14:20

6 Answers6

48

Anything but return this == other would be counter intuitive and violate the principle of least astonishment. Two enum constants are expected to be equal if and only if they are the same object and the ability to override this behavior would be error prone.

Same reasoning applies to hashCode(), clone(), compareTo(Object), name(), ordinal(), and getDeclaringClass().


The JLS does not motivate the choice of making it final, but mentions equals in the context of enums here. Snippet:

The equals method in Enum is a final method that merely invokes super.equals on its argument and returns the result, thus performing an identity comparison.

aioobe
  • 413,195
  • 112
  • 811
  • 826
3

There is already provides a strong intuitive notion of what it means for instances (values) of an enum to be equal. Allowing the overloading the equals method would lead to that notion being violated, leading to unexpected behavior, bugs and so on.

Stephen C
  • 698,415
  • 94
  • 811
  • 1,216
2

Sometimes we need to deal with data that does not conform to Java naming standards. It would be nice to be able to do something like this:

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);
    }
}

The "String" class would need to be modified to accommodate...

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
    1 question though, why would you not have the client code do the valueOf string first and then compare the resulting Enum? That way we are still comparing Enums and we dont need to fidget around the equals and hasCodes – Ashwin Prabhu Jan 10 '19 at 05:52
2

I am going the majority opinion. If the override of Enum::equals is forbidden, I don't think it was to follow the principle of least astonishment. I think it is to not break Java.

In the JVM, and in some old classes such like EnumMap, Enum identities may be coded internally with an int. For example, if we allow to override Enum::equals, then EnumMap<K, V> would break the contract of Map.

mcoolive
  • 3,805
  • 1
  • 27
  • 32
1

It is precisely because the Java designers could not think of any conceivable use case for overriding Enum.equals(Object) that that method is declared as final - so that such overriding would be impossible.

Jean Hominal
  • 16,518
  • 5
  • 56
  • 90
0

I must confess enums are the last thing I would want to override equals() in.

I think the reason equals() is final in enums is that Java encourages == for enum comparison, and the implementation of equals() in enums simply uses it, So allowing equals() from being overridden is to prevent == and equals() from behaving differently, which is something other developers would not expect.

Oak
  • 26,231
  • 8
  • 93
  • 152