Какая часть общего договора equals() делает мои равенства() не удовлетворяет

Я новичок в java и просто пытаюсь понять идею @Override методов equals() и hashcode().
Я знаю, что метод equals должен быть правильным:

  • Рефлексивный: a.equals(a)
  • Симметричный: a.equals(b) , затем b.equals(a)
  • Transitive: a.equals(b) && b.equals(c) Затем a.equals(c)
  • Не null: ! a.equals(null)

Я изо всех сил пытаюсь определить, какое из указанных выше свойств я есть, и не удовлетворительно, когда пишу свое переопределение метода equals.

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

Я выписал то, что я думаю, это правильный способ сделать это, но когда я проверяю сгенерированную версию eclipse, я, кажется, "не хватает" некоторых аспектов.

Пример:

public class People {

    private Name first; //Invariants --> !Null, !=last
    private Name last;  // !Null, !=first
    private int age;    // !Null, ! <=0
    ...
}

Что я написал:

public boolean equals(Object obj){
    if (obj == null){
        return false;
    }
    if (!(obj instanceof People)){
         return false;
    }
    People other = (People) obj;
    if (this.age != other.age){
        return false;
    }
    if (! this.first.equals(other.first)){
        return false;
    }
    if (! this.last.equals(other.last)){
        return false;
    }
    return true;
}

vs генерируемое затмение

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

    People other = (People) obj;
    if (first == null) {
        if (other.first != null)
            return false;
    } else if (!first.equals(other.first))
        return false; 
    if (age != other.age)
        return false;
    if (last == null) {
        if (other.last != null)
            return false;
    } else if (!last.equals(other.last))
        return false;

    return true;
}

Мне не хватает:

  • if (this == obj)
        return true;
    
  • if (getClass() != obj.getClass())
        return false;
    
  • И для каждой переменной:

    if (first == null) {
        if (other.first != null)
            return false;
    } else if (!first.equals(other.first))
        return false;
    

Я не уверен, что getClass() является и неверно ли я имплантирован?

Ответы

Ответ 1

Первый фрагмент кода:

if (this == obj)
    return true;

Это повышает производительность, если вы сравниваете ссылку на объект с самим собой. Пример: a.equals(a);.

Второй фрагмент кода:

if (getClass() != obj.getClass())
    return false;

Это сравнивается, если класс сравниваемой ссылки является тем же классом this. Разница между использованием этого подхода и instanceof заключается в том, что он более ограничительный при сравнении с подклассом. Пример:

public class Foo { }
public class Bar extends Foo { }

//...
Foo foo = new Bar();
System.out.println(foo instanceof Bar); //prints true
System.out.println(foo instanceof Foo); //prints true
Foo foo2 = new Foo();
System.out.println(foo.getClass() == foo2.getClass()); //prints false

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

Третий фрагмент кода:

if (first == null) {
        if (other.first != null)
            return false;
    } else if (!first.equals(other.first))
        return false; //For each variable.

Это просто нулевая проверка для каждого поля ссылки объекта в классе. Обратите внимание, что если this.first равно null, то выполнение this.first.equals(...) будет вызывать NullPointerException.

Ответ 2

Я не думаю, что ваша реализация неверна, но несколько примечаний:

if (this == obj)
      return true;

Является ли оптимизация производительности, она непосредственно тестирует тесты ссылочного равенства и коротких замыканий, где a a.

if (getClass() != obj.getClass())
        return false;

похож на ваш вызов instanceof, оптимизирует нулевую проверку. Другие вызовы кажутся нулевыми.

Ответ 3

Вам не нужно писать

if (obj == null){
        return false;
}
if (!(obj instanceof People)){
     return false;
}

потому что null всегда дает false в тегах instanceof. Таким образом, эти строки могут быть упрощены до простого

if (!(obj instanceof People)){
     return false;
}

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

public class SpecialPeople extends People {

    // code omitted

    @Override
    public boolean equals(Object object) {
        if (object == null || object.getClass() != getClass())
            return false;
        SpecialPeople other = (SpecialPeople) object;
        return other.getAge() == getAge() 
                && other.getFirst().equals(getFirst()) 
                && other.getLast().equals(getLast());
}

Теперь предположим, что a является экземпляром People, а b является экземпляром SpecialPeople. Предположим также, что a и b имеют одинаковое имя и возраст. Тогда

a.equals(b) == true  // instanceof check succeeds
b.equals(a) == false // getClass() check fails

Поэтому equals() не является симметричным! По этой причине, если вы используете instanceof, а не getClass() в equals(), вы, вероятно, должны либо сделать метод equals() final, либо класс final.