Почему Java не разрешает переопределять равные (Object) в Enum?

Я заметил, что следующий фрагмент...

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

... не разрешено для Enum, так как метод equals(Object x) определяется как final в Enum. Почему это так?

Я не могу придумать какой-либо прецедент, который потребует переопределения equals(Object) для Enum. Мне просто интересно узнать причину такого поведения.

Ответы

Ответ 1

Любое, кроме return this == other, будет противоречивым и нарушит принцип наименьшего удивления. Ожидается, что две константы перечисления будут equal если и только если они являются одним и тем же объектом, и возможность переопределить это поведение будет подвержена ошибкам.

То же самое относится и к hashCode(), clone(), compareTo(Object), name(), ordinal() и getDeclaringClass().


JLS не мотивирует выбор сделать его окончательным, но упоминает о равных в контексте перечислений здесь. Snippet:

Метод equals в Enum - это последний метод, который просто вызывает super.equals для своего аргумента и возвращает результат, таким образом выполняя сравнение идентичности.

Ответ 2

Уже существует сильное интуитивное представление о том, что означает для экземпляров (значений) enum равным. Разрешение перегрузки метода equals приведет к нарушению этого понятия, что приведет к неожиданному поведению, ошибкам и т.д.

Ответ 3

Иногда нам приходится иметь дело с данными, которые не соответствуют стандартам именования 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;
}

Ответ 4

Именно потому, что разработчики Java не могли придумать какой-либо мыслимый вариант использования для переопределения Enum.equals(Object), что этот метод объявлен как final, так что такое переопределение было бы невозможным.

Ответ 5

Я должен признаться, что перечисления - это последнее, что я хотел бы переопределить equals() in.

Я думаю, что причина equals() является окончательной в перечислениях, так это то, что Java поощряет == для сравнения enum, а реализация equals() в перечислениях просто использует ее, поэтому разрешение equals() на переопределение - t22 > и equals() от поведения по-другому, чего не ожидали бы другие разработчики.