java 中覆盖equals() 方法
在java中覆盖 equals()
起因
这周软件构造实验中第三题需要判断自建类的相等性.
例如如下代码
class Person{
String name;
public Person(String name){
this.name = name;
}
}
Person aperson = new Person("a")
Person bperson = new Person("a")
我们希望的结果是aperson 等于 bperson.
解决方法
1. 使用 ==
java中 a == b
判断a 和 b 是否引用同一个object, 所以aperson == bperson
会返回false
, 这种方法不行
2. 覆盖euqals()
因为java中的所有class 都直接或间接地继承自 Object 类, 而Object类有一些基本的方法如 equals(), toString()等等.....我们就可以覆盖其中的equals方法, 然后调用aperson.equals(bperson)
来判断两者是否相等.
我的第一次实现如下
class Person{
String name;
public Person(String name){
this.name = name;
}
@Override
public boolean equals(Object o){
return this.name == o.name;
}
}
但是第8行return this.name == o.name;
处报错了 name can't be resolved or not a field
于是我又换了种写法:
class Person{
String name;
public Person(String name){
this.name = name;
}
@Override
public boolean equals(Person o){
return this.name == o.name;
}
}
然而这次第七行public boolean equals(Person o)
处又报错了The method equals(Person) of type Person must override or implement a supertype method
这两个错误的原理我不是很清楚, 等之后我弄清楚之后会更新上来
于是我只好再改个方法如下
class Person{
String name;
public Person(String name){
this.name = name;
}
@Override
public boolean equals(Object o){
return this.name == o;
}
}
这次倒是没有报错了, 不过调用的时候非常不美观, 得写成aperson.equals(bperson.name)
看着十分不舒服, 我又上网搜索覆盖equals的相关博客, 找到了这个博客, 并根据其改写了最终版本如下:
class Person{
String name;
public Person(String name){
this.name = name;
}
@Override
public boolean equals(Object o) {
if(this == o) {
return true;
}
if(!(o instanceof Person)) {
return false;
}
Person person = (Person) o;
return this.name.equals(person.name);
}
}
终于,aperson.equals(bperson)
可以返回true
了
更新
第一次失败的原因
Object
类没有name
域
[参考java核心技术5.1.1-5.1.2(第十版)]
第二次失败的原因
Object
类中的equals
方法的参数类型是Object
, 只有函数签名相同的才能覆盖, 这儿写成public boolean equals(Person o)
相当于自己实现了一个equals
方法, 和前面的@override
矛盾.
[参考java核心技术5.1.1-5.1.2(第十版)]
改进
当 b 为 a 的子类对象的时候, b isinstanceof a
会返回true
, 某些情况下这样不利于equals
要求的对称性, 这种情况下可以考虑使用getClass()
方法
附上java核心技术中给的编写完美地equals方法的建议:
1 ) 显式参数命名为 otherObject, 稍后需要将它转换成另一个叫做 other 的变量。
2 ) 检测 this 与 otherObject 是否引用同一个对象:
if (this = otherObject) return true;
这条语句只是一个优化。实际上,这是一种经常采用的形式。因为计算这个等式要比一个一个地比较类中的域所付出的代价小得多。
3 ) 检测 otherObject 是否为 null, 如 果 为 null, 返 回 false。这项检测是很必要的.
if (otherObject = null) return false;
4 ) 比较 this 与 otherObject 是否属于同一个类。如果 equals 的语义在每个子类中有所改
变,就使用 getClass 检测:
if (getClass() != otherObject.getCIassO) return false;
如果所有的子类都拥有统一的语义,就使用 instanceof 检测:
if (!(otherObject instanceof ClassName)) return false;
5 ) 将 otherObject 转换为相应的类类型变量:
ClassName other = (ClassName) otherObject
6 ) 现在开始对所有需要比较的域进行比较了。使用= 比较基本类型域,使用 equals 比较对象域 。如果所有的域都匹配, 就返回 true; 否 则 返 回 false。
return field == other.field && Objects.equa1s(fie1d2, other.field2)
如果在子类中重新定义 equals, 就要在其中包含调用 super.equals(other)