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方法里两次输出结果不一致,但按照逻辑来说,父类对象本不应该与子类对象相等

ios oc 重写父类属性的set get方法_ide

 

说好的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的童鞋一定经常看见这个方法吧,如图

ios oc 重写父类属性的set get方法_System_02

经查,它可以判断该对象的类型,因此利用好它,就不用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));
	}
}

结果如我们预期

ios oc 重写父类属性的set get方法_System_03

好,这个问题到此已经解决了。但是另外需注意的是,重写equals方法最好把hashCode方法给重写了,否则在使用Set.contains()等方法时会很不好使,因为这类方法会同时调用对象的hashCode方法和equals方法进行处理。

总结:重写equals方法直接使用Object.getClass()方法判断类型而不要用instanceof