我特别关注遵守在 where 中建立的一般合同的对称部分Object#equals(Object)
,给定两个非空对象x
and , andy
的结果应该是相同的。x.equals(y)
y.equals(x)
假设您有两个类,PurchaseOrder
并且InternationalPurchaseOrder
后者扩展了前者。false
在基本情况下,将每个实例与另一个实例进行比较应该一致地返回是有意义的,x.equals(y)
并且y.equals(x)
仅仅因为 aPurchaseOrder
并不总是 anInternationalPurchaseOrder
因此,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
。在《Effective Java》一书中,Joshua Bloch 间接警告了equals
不必要的覆盖。然而,在这种情况下,子类必须有一个覆盖equals
实例以比较子类中声明的字段。
这已经够久了。这是更复杂的equals
函数实现的情况,还是仅仅是反对向上转换的情况?
澄清:@Progman 在下面的评论部分提出的建议没有回答这个问题。这不是覆盖equals
子类方法的简单案例。我认为我在这里发布的代码表明我已经正确地做到了这一点。这篇文章特别是关于比较两个对象的预期结果,当一个对象被向上转换时,它的行为就像超类的对象一样。