Порівнюючи об’єкти на Java, ви робите семантичну перевірку , порівнюючи тип та ідентифікуючи стан об'єктів з:
- себе (той самий екземпляр)
- сам (клон або реконструйована копія)
- інші об'єкти різного типу
- інші об'єкти одного типу
null
Правила:
- Симетрія :
a.equals(b) == b.equals(a)
equals()
завжди приносить true
або false
, але ніколи NullpointerException
, ClassCastException
або будь-який інший викид
Порівняння:
- Перевірка типу : обидва екземпляри повинні бути одного типу, тобто вам потрібно порівняти фактичні класи для рівності. Це часто неправильно реалізується, коли розробники використовують
instanceof
для порівняння типів (що працює лише до тих пір, поки немає підкласів, і порушує правило симетрії, коли A extends B -> a instanceof b != b instanceof a)
.
- Семантична перевірка ідентифікаційного стану : переконайтеся, що ви розумієте, за яким станом ідентифікуються екземпляри. Особи можуть бути визначені за номером соціального страхування, але не за кольором волосся (можна фарбувати), назвою (можна змінити) або віком (постійно змінюється). Тільки із об'єктами значення слід порівнювати повний стан (усі неперехідні поля), інакше перевіряйте лише те, що ідентифікує екземпляр.
Для вашого Person
класу:
public boolean equals(Object obj) {
// same instance
if (obj == this) {
return true;
}
// null
if (obj == null) {
return false;
}
// type
if (!getClass().equals(obj.getClass())) {
return false;
}
// cast and compare state
Person other = (Person) obj;
return Objects.equals(name, other.name) && Objects.equals(age, other.age);
}
Багаторазовий корисний клас:
public final class Equals {
private Equals() {
// private constructor, no instances allowed
}
/**
* Convenience equals implementation, does the object equality, null and type checking, and comparison of the identifying state
*
* @param instance object instance (where the equals() is implemented)
* @param other other instance to compare to
* @param stateAccessors stateAccessors for state to compare, optional
* @param <T> instance type
* @return true when equals, false otherwise
*/
public static <T> boolean as(T instance, Object other, Function<? super T, Object>... stateAccessors) {
if (instance == null) {
return other == null;
}
if (instance == other) {
return true;
}
if (other == null) {
return false;
}
if (!instance.getClass().equals(other.getClass())) {
return false;
}
if (stateAccessors == null) {
return true;
}
return Stream.of(stateAccessors).allMatch(s -> Objects.equals(s.apply(instance), s.apply((T) other)));
}
}
Для вашого Person
класу, використовуючи цей клас корисності:
public boolean equals(Object obj) {
return Equals.as(this, obj, t -> t.name, t -> t.age);
}