在对象的.equals(Object)
javadoc:
它是对称的:对于任何非空参考值x和y, 当且仅当y.equals(x)返回时,x.equals(y)才应返回true 真。
在示例代码中几乎无处不在我看到覆盖.equals(Object)
方法,它使用instanceof
作为首批测试之一,例如:What issues / pitfalls must be considered when overriding equals and hashCode?
public class Person {
private String name;
private int age;
public boolean equals(Object obj) {
if (obj == null)
return false;
if (obj == this)
return true;
if (!(obj instanceof Person))
return false;
...
}
}
现在class SpecialPerson extends Person
位于equals
:
if (!(obj instanceof SpecialPerson))
return false;
我们不保证.equals()
是对称的。
例如,这里已经讨论过:any-reason-to-prefer-getclass-over-instanceof-when-generating-equals
Person a = new Person(), b = new SpecialPerson();
a.equals(b); //sometimes true, since b instanceof Person
b.equals(a); //always false
也许我应该在SpecialPerson的开头添加等于直接调用super?
public boolean equals(Object obj) {
if( !obj instanceof SpecialPerson )
return super.equals(obj);
...
/* more equality tests here */
}
答案 0 :(得分:9)
许多示例使用instanceof
有两个原因:a)它将空检查折叠并将check键入一个或b)该示例适用于Hibernate或其他一些代码重写框架。
“正确”(根据JavaDoc)解决方案是使用this.getClass() == obj.getClass()
。这适用于Java,因为类是单例,VM保证这一点。如果你是偏执狂,你可以使用this.getClass().equals(obj.getClass())
,但这两者实际上是等价的。
大部分时间都可以使用。但有时,Java框架需要使用字节代码做“聪明”的事情。这通常意味着它们会自动创建子类型。由于子类型应该被认为与原始类型相同,equals()
必须以“错误”的方式实现,但这无关紧要,因为在运行时,子类型都将遵循某些模式。例如,他们会在调用setter之前做其他事情。这对“平等”没有影响。
正如您所注意到的,当您遇到这两种情况时,事情开始变得丑陋:您真正扩展了基本类型,并将其与自动子类型生成混合在一起。如果这样做,则必须确保永远不要使用非叶类型。
答案 1 :(得分:1)
你在这里遗漏了一些东西。我将尝试强调这一点:
假设您有Person person = new Person()
和Person personSpecial = new SpecialPerson()
,那么我相信您不希望这两个对象相等。所以,它真的按要求工作,等于必须返回false。
此外,对称性指定两个类中的equals()
方法必须同时遵守它。如果一个等于返回true而其他返回false,那么我会说这个缺陷在等于覆盖。
答案 2 :(得分:0)
您尝试解决问题的方法不正确。假设您有2个子类SpecialPerson
和BizarrePerson
。通过此实现,BizarrePerson
个实例可以等于SpecialPerson
个实例。你通常不希望这样。
答案 3 :(得分:0)
不要使用instanceof
。请改用this.getClass() == obj.getClass()
。那么你正在检查这个确切的类。
使用equals
时,您应该始终使用hashCode
并覆盖它!
Person的hashCode方法可能如下所示:
@Override
public int hashCode()
{
final int prime = 31;
int result = 1;
result = prime * result + age;
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}
并在你的equals方法中使用它:
if (this.hashCode() != obj.hashCode())
{
return false;
}
答案 4 :(得分:0)
类型不应该认为自己等于任何其他类型的对象 - 甚至是子类型 - ,除非两个对象派生自一个公共类,其合约指定不同类型的后代应该如何检查相等性
例如,抽象类StringyThing
可以封装字符串,并提供执行转换为字符串或提取子字符串等方法的方法,但不对支持格式强加任何要求。例如,StringyThing
的一个可能的子类型可能包含StringyThing
的数组,并封装所有这些字符串的串联值。如果转换为字符串会产生相同的结果,则StringyThing
的两个实例将被定义为相等,并且两个其他无法区分的StringyThing
实例之间的比较可能不得不依赖于它们,但是StringyThing
- 派生类型可以包括优化各种情况的代码。例如,如果一个StringyThing
表示“M
重复字符ch
”而另一个表示“N
重复字符串St”,后一个类型知道第一个,它可以检查St
是否只包含M/N
重复的字符ch
。这样的检查将指示字符串是否相等,而不必“扩展”它们中的任何一个。