Я особенно заинтересован в соблюдении части симметрии общего контракта, установленного в Object#equals(Object)
, где для двух ненулевых объектов x
и y
результат x.equals(y)
и y.equals(x)
должен быть одинаковым.
Предположим, у вас есть два класса, PurchaseOrder
и InternationalPurchaseOrder
, где последний расширяет первый. В базовом случае имеет смысл, что сравнение экземпляра каждого с другим должно последовательно возвращать false
для x.equals(y)
и y.equals(x)
просто потому, что PurchaseOrder
не всегда является InternationalPurchaseOrder
и, следовательно, дополнительные поля в объектах InternationalPurchaseOrder
не будут присутствовать в экземплярах InternationalPurchaseOrder
. PurchaseOrder
.
Теперь предположим, что вы повышали экземпляр InternationalPurchaseOrder
.
PurchaseOrder order1 = new PurchaseOrder();
PurchaseOrder order2 = new InternationalPurchaseOrder();
System.out.println("Order 1 equals Order 2? " + order1.equals(order2));
System.out.println("Order 2 equals Order 1? " + order2.equals(order1));
Мы уже установили, что результат должен быть симметричным. Но должен ли результат быть false
для случаев, когда оба объекта содержат одни и те же внутренние данные? Я считаю, что результат должен быть true
независимо от того, что у одного объекта есть дополнительное поле. Поскольку при преобразовании order2
доступ к полям в классе InternationalPurchaseOrder
ограничивается, результатом метода equals()
должен быть результат вызова super.equals(obj)
.
Если все, что я сказал, верно, реализация метода equals
в InternationalPurchaseOrder
должна быть примерно такой:
@Override
public boolean equals(Object obj) {
if (!super.equals(obj)) return false;
// PurchaseOrder already passed check by calling super.equals() and this object is upcasted
InternationalPurchaseOrder other = (InternationalPurchaseOrder)obj;
if (this.country == null) {
if (other.country != null) return false;
} else if (!country.equals(other.country)) return false;
return true;
}
Предположим, что country
— единственное поле, объявленное в этом подклассе.
Проблема в суперклассе
@Override
public boolean equals (Object obj) {
if (this == obj) return true;
if (obj == null) return false;
if (getClass() != obj.getClass()) return false;
PurchaseOrder other = (PurchaseOrder) obj;
if (description == null) {
if (other.description != null) return false;
} else if (!description.equals(other.description)) return false;
if (orderId == null) {
if (other.orderId != null) return false;
} else if (!orderId.equals(other.orderId)) return false;
if (qty != other.qty) return false;
return true;
}
Потому что два экземпляра не принадлежат к одному классу. И если вместо этого я использую getClass().isAssignableFrom(Class)
, симметрия теряется. То же самое верно и при использовании instanceof
. В книге «Эффективная Java» Джошуа Блох косвенно предупреждает о ненужном переопределении equals
. В этом случае, однако, необходимо, чтобы подкласс имел переопределенное equals
для экземпляров для сравнения полей, объявленных в подклассе.
Это произошло достаточно давно. Является ли это аргументом в пользу более сложной реализации функции equals
или это просто аргумент против повышения приведения?
ПОЯСНЕНИЕ: на этот вопрос нет ответа в предложениях, предложенных @Progman в разделе комментариев ниже. Это не простой случай переопределения методов equals
для подклассов. Я думаю, что код, который я разместил здесь, показывает, что я сделал это правильно. Этот пост СПЕЦИАЛЬНО посвящен ожидаемому результату сравнения двух объектов, когда один из них преобразован в более высокий класс, чтобы он вел себя как объект суперкласса.
order2
по-прежнему ссылается на объектInternationalPurchaseOrder
, аorder2.equals(order1)
вызывает методequals()
из классаInternationalPurchaseOrder
. Что заставляет методequals()
возвращатьfalse
с вашего предложения [...] должен последовательно возвращатьfalse
дляx.equals(y)
иy.equals(x)
просто потому, чтоPurchaseOrder
не всегда являетсяInternationalPurchaseOrder
и, следовательно, [...]. Как вы планируете вернутьtrue
, если вы также говорите, что он должен возвращатьfalse
? - person Progman   schedule 07.03.2021