При сравнении объектов в 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);
}