Подтвердить что ты не робот

Java.equals() instanceof подкласс? Почему бы не назвать superclass equals вместо того, чтобы сделать его окончательным?

В Object .equals(Object) указано: javadoc:

Он симметричен: для любых непустых опорных значений x и y, x.equals(y) должно возвращать true тогда и только тогда, когда y.equals(x) возвращает правда.

Почти везде в примере кода я вижу переопределенный метод .equals(Object), который использует instanceof как один из первых тестов, например здесь: Какие проблемы/ловушки необходимо учитывать при переопределении equals и hashCode?

public class Person {
    private String name;
    private int age;

    public boolean equals(Object obj) {
        if (obj == null)
            return false;
        if (obj == this)
            return true;
        if (!(obj instanceof Person))
            return false;
        ...
    }

}

Теперь с class SpecialPerson extends Person с equals:

        if (!(obj instanceof SpecialPerson))
            return false;

мы не гарантируем, что .equals() является симметричным. Здесь обсуждалось здесь: any-reason-to-prefer-getclass-over-instanceof-when-generate-equals

Person a = new Person(), b = new SpecialPerson();

a.equals(b);    //sometimes true, since b instanceof Person
b.equals(a);    //always false

Может быть, я должен добавить в начало SpecialPerson равный прямой вызов super?

    public boolean equals(Object obj) {
        if( !obj instanceof SpecialPerson )
            return super.equals(obj);
        ... 
        /* more equality tests here */
    }
4b9b3361

Ответ 1

В большинстве примеров используется instanceof по двум причинам: a) он сбрасывает нулевую проверку и проверяет тип на один или b) пример для Hibernate или какой-либо другой структуры перезаписи кода.

"Правильное" (согласно JavaDoc) решение - использовать this.getClass() == obj.getClass(). Это работает для Java, потому что классы являются одиночными, и VM гарантирует это. Если вы параноик, вы можете использовать this.getClass().equals(obj.getClass()), но оба они действительно эквивалентны.

Это работает большую часть времени. Но иногда Java-фреймворкам необходимо делать "умные" вещи с помощью байтового кода. Обычно это означает, что они автоматически создают подтип. Поскольку подтип должен считаться равным исходному типу, equals() должен быть реализован "неправильным" способом, но это не имеет значения, поскольку во время выполнения подтипы будут следовать определенным шаблонам. Например, они будут делать дополнительные вещи перед вызовом сеттера. Это не влияет на "равенство".

Как вы заметили, все начинает становиться уродливым, когда у вас есть оба случая: вы действительно расширяете базовые типы, и вы смешиваете их с автоматическим подтипом. Если вы это сделаете, вы должны убедиться, что никогда не используете нелистные типы.

Ответ 2

Вам что-то не хватает. Я попытаюсь выделить это:

Предположим, что у вас есть Person person = new Person() и Person personSpecial = new SpecialPerson(), тогда я уверен, что вам не понравятся эти два объекта. Итак, он действительно работает по мере необходимости, равный должен возвращать false.

Кроме того, симметрия указывает, что метод equals() в обоих классах должен подчиняться ему в одно и то же время. Если кто-то равен true, а другой возвращает false, то я бы сказал, что недостаток находится в равном значении.

Ответ 3

Ваша попытка решить проблему неверна. Предположим, у вас есть 2 подкласса SpecialPerson и BizarrePerson. При этой реализации экземпляры BizarrePerson могут быть равны экземплярам SpecialPerson. Обычно вы этого не хотите.

Ответ 4

не использовать instanceof. вместо этого используйте this.getClass() == obj.getClass(). то вы проверяете этот точный класс.

при работе с equals вы всегда должны использовать hashCode и переопределить это тоже!

метод hashCode для Person может выглядеть следующим образом:

@Override
public int hashCode()
{
    final int prime = 31;
    int result = 1;
    result = prime * result + age;
    result = prime * result + ((name == null) ? 0 : name.hashCode());
    return result;
}

и используйте его в методе equals:

if (this.hashCode() != obj.hashCode())
{
    return false;
}

Ответ 5

Тип не должен считать себя равным объекту любого другого типа - даже подтипа - если оба объекта не выведены из общего класса, контракт которого указывает, как потомки разных типов должны проверять равенство.

Например, абстрактный класс StringyThing может инкапсулировать строки и предоставлять методы для выполнения таких операций, как преобразование в строку или выпрямление подстрок, но не накладывание каких-либо требований в формате поддержки. Один возможный подтип StringyThing, например, может содержать массив StringyThing и инкапсулировать значение конкатенации всех этих строк. Два экземпляра StringyThing будут определены как равные, если преобразование в строки даст одинаковые результаты, а сравнение между двумя экземплярами, отличными от других, StringyThing, типы которых ничего не знают друг о друге, возможно, придется отпасть от этого, но StringyThing Приведенные типы могут включать код для оптимизации различных случаев. Например, если один StringyThing представляет "M повторения символа ch", а другой представляет "N повторения строки St", и последний тип знает о первом, он мог бы проверить, будет ли St содержит только M/N повторения символа ch. Такая проверка будет указывать, равны ли строки, без необходимости "расширять" одну из них.