PS:这篇文章内容比较简单,不想看内容的可以直接滑到最底下看结论
从equals方法说起
1.当我们写了一个类名叫Father并重写了equals方法时,我们或许会这样写
static class Father {
String name = "666";
@Override
public boolean equals(Object obj) {
if (obj instanceof Father) {
Father that = (Father) obj;
if (this.name == that.name || this.name.equals(that.name))
return true;
}
return false;
}
}
public static void main(String[] args) {
Father father = new Father();
Father father2 = new Father();
System.out.println(father.equals(father2));
}
当然,在main方法里输出结果为true
2.后来我们定义了一个子类为Child,equals方法与父类一样写
static class Child extends Father {
String name = "666";
@Override
public boolean equals(Object obj) {
if (obj instanceof Father) {
Child that = (Child) obj;
if (this.name == that.name || this.name.equals(that.name))
return true;
}
return false;
}
}
public static void main(String[] args) {
Child child = new Child();
Child child2 = new Child();
System.out.println(child.equals(child2));
}
当然,在main方法里输出结果还是为true
3.然后我们把Child对象与Father对象使用equals方法比较
public class Test {
static class Father {
String name = "666";
@Override
public boolean equals(Object obj) {
if (obj instanceof Father) {
Father that = (Father) obj;
if (this.name == that.name || this.name.equals(that.name))
return true;
}
return false;
}
}
static class Child extends Father {
String name = "666";
@Override
public boolean equals(Object obj) {
if (obj instanceof Child) {
Child that = (Child) obj;
if (this.name == that.name || this.name.equals(that.name))
return true;
}
return false;
}
}
public static void main(String[] args) {
Father father = new Father();
Child child = new Child();
System.out.println(father.equals(child));
System.out.println(child.equals(father));
}
}
可以看到,main方法里两次输出结果不一致,但按照逻辑来说,父类对象本不应该与子类对象相等
说好的equals方法的对称性呢,这是为何?哦原来是父类的equals方法里的写法
@Override
public boolean equals(Object obj) {
if (obj instanceof Father) {
Father that = (Father) obj;
if (this.name == that.name || this.name.equals(that.name))
return true;
}
return false;
}
可以看到我们使用了instanceof Father的判断方法进入了可能return true的分叉,而Child类本来就是继承Father类,所以Child属于Father类,因此满足了判断。要如何规避这个错误呢?
笔者的思路:
1.既然子类对象调用equals方法判断是否与父类的对象相等时一定不满足instanceof Child,那么就不从子类考虑。
2.考虑在父类调用equals方法时,添加if(obj instanceof Child) return false;这样的语句。但是这样的结果就是,在子类很多时,你不可能一一列举,因此这个思路不正确。
3.要看obj是不是本类,如何判断呢?除了instanceof以外有什么好办法?java有一个getClass()方法,我相信经常使用IDE的童鞋一定经常看见这个方法吧,如图
经查,它可以判断该对象的类型,因此利用好它,就不用instanceof了,以后所有equals方法都写成if(obj.getClass().equals(this.getClass())即可,日后不用再改
代码如下
public class Test {
static class Father {
String name = "666";
@Override
public boolean equals(Object obj) {
if (obj.getClass().equals(this.getClass())) {
Father that = (Father) obj;
if (this.name == that.name || this.name.equals(that.name))
return true;
}
return false;
}
}
static class Child extends Father {
String name = "666";
@Override
public boolean equals(Object obj) {
if (obj.getClass().equals(this.getClass())) {
Child that = (Child) obj;
if (this.name == that.name || this.name.equals(that.name))
return true;
}
return false;
}
}
public static void main(String[] args) {
Father father = new Father();
Father father2 = new Father();
Child child = new Child();
Child child2 = new Child();
System.out.println(father.equals(father2));
System.out.println(child.equals(child2));
System.out.println(father.equals(child));
System.out.println(child.equals(father));
}
}
结果如我们预期
好,这个问题到此已经解决了。但是另外需注意的是,重写equals方法最好把hashCode方法给重写了,否则在使用Set.contains()等方法时会很不好使,因为这类方法会同时调用对象的hashCode方法和equals方法进行处理。
总结:重写equals方法直接使用Object.getClass()方法判断类型而不要用instanceof