Какая часть общего договора 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
.