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

17

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

  1. Рефлексивный: a.equals(a)
  2. Симметричный: a.equals(b) , затем b.equals(a)
  3. Транзитив: a.equals(b) && b.equals(c) Затем a.equals(c)
  4. Не 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() является и неверно ли я имплантирован?

    
задан Dan 15.04.2015 в 06:18
источник
  • Почему вы бросаете имя в реализацию равных для людей? Похож на опечатку. Просьба уточнить. –  Radiodef 15.04.2015 в 06:22
  • Вы также можете увидеть «Любые причины, чтобы предпочесть getClass () над instanceof при генерации .equals ()?» Целый ответ можно написать только по этому вопросу. –  Radiodef 15.04.2015 в 06:27
  • У вас все еще есть if (! (obj instanceof Name)) {строка неверна –  Noman_1 15.04.2015 в 13:02

3 ответа

8

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

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 % %

ответ дан Luiggi Mendoza 15.04.2015 в 06:25
  • Если вам интересно, почему в последнем фрагменте не используется Objects.equals ... эта часть Eclipse, вероятно, была написана до добавления Objects.equals (в Java 7). –  immibis 15.04.2015 в 09:09
4

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

if (this == obj)
      return true;

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

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

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

    
ответ дан Elliott Frisch 15.04.2015 в 06:22
  • Похоже, но не то же самое. instanceof истинно для подкласса, который может нарушить правило «Симметричное». –  pkalinow 15.04.2015 в 09:46
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 .     

ответ дан Paul Boddington 15.04.2015 в 06:47
  • Если вы используете obj.getClass, сначала нужно сначала проверить значение null. Зависит от aproach, который вы берете о getClass или instanceOf –  Noman_1 15.04.2015 в 13:04
  • @ Noman_1 Сначала я проверил значение null. –  Paul Boddington 15.04.2015 в 13:09