Java基础知识之equals方法

《Java核心技术》

1.equals方法介绍以及重写

Object类中的equals方法是用于检测两个对象是否相等,在Object类中equals方法的具体实现是确定两个对象引用是否相等。不过,我们经常需要基于状态检测对象的相等性,如果两个对象的状态相同,才认为这两个对象相等。
例如,如果两个员工对象的姓名、薪水和雇佣日期都一样,就认为它们相等。

public class Employee {
	...
	public boolean equals(Object otherObject) {
		if (this == otherObject) return true;
		
		if (otherObject == null) return false;

		if (getClass() != otherObject.getClass()) return false;

		Employee other = (Employee)otherObject;
		return name.equals(other.name)
			&& salary == other.salary
			&& hireday.equals(other.hireDay);
	}
}

为了防止name或hireDay可能为null的情况,需要使用Objects.equals方法。
Objects.equals:如果两个参数都为null,Objects.equals(a, b)调用将返回true;如果其中一个参数为null,返回false;如果两个参数都不为null,则调用a.equals(b)。
Employee.equals方法最后一句改写为:

return Objects.equals(name, other.name)
	&& salary == other.salary
	&& Objects.equals(hireDay, other.hireDay);

在子类中定义equals方法时,首先要调用超类的equals。如果超类中的字段相等,再比较子类中的实例字段。

public class Manager extends Employee {
	...
	public boolean equals(Object ohterObject) {
		if (!super.equals(ohterObject)) return false;
		Manager other = (Manager)ohterObject;
		return bonus == ohter.bonus;
	}
}

2.相等测试与继承

调用上述的equals方法,如果隐式参数和显式参数不属于同一个类,equals方法就会返回false。但是,许多人却喜欢用instanceof进行检测:

if (!(ohterObject instanceof Employee)) return false;

这样就允许otherObject属于一个子类。但是这种方法可能会招致一些麻烦,不建议采用这种处理方式。
在Java语言规范中要求equals方法具有下面的特性:

  1. 自反性:对于任何非空引用x,x.equals(x)应返回true。
  2. 对称性:对于任何引用x和y,当且仅当y.equals(x)返回true时,x.equals(y)返回true。
  3. 传递性:对于任何引用x,y和z,如果x.equals(y)返回true,y.equals(z)返回true,x.equals(z)也应该返回true。
  4. 一致性:如果x和y引用的对象没有发生变化,反复调用x.equals(y)应该返回相同的结果。
  5. 对于任意非空引用x,x.equals(null)应该返回false。

在上述的对称性规则中,当参数不属于同一类的时候会有一些微妙的结果。现在有下面这个调用:

e.equals(m)

这里的e是一个Employee对象,m是一个Manager对象,并且两个对象有相同的姓名、薪水和雇佣日期。如果在Employee.equals中用instanceof进行检测,这个调用将返回true。反过来调用:

m.equals(e)

Emmm…有点奇怪,对称性规则不允许这个方法调用返回false或者抛出异常,这样使得Manager类的equals方法必须愿意将自己与任何一个Employee对象进行比较,然而,Employee对象并没有经理特有的那部分信息,这显得instanceof并不是那么好。

至此,有两种完全不同的情形:

  • 如果子类可以有自己的相等性概念,则对称性需求将强制使用getClass检测。
  • 如果由超类决定相等性概念,那么就可以使用instanceof检测,这样可以在不同子类的对象之间进行相等性比较。

3.编写一个完美的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.getClass()) return false;
    如果所有的子类都有相同的相等性语义,可以使用instanceof检测:
    if (!(otherObject instanceof ClassName)) return false;
  5. 将otherObject强制转换为相应类类型的变量:ClassName other = (ClassName) otherObject;
  6. 根据相等性概念的要求来比较字段。使用 ‘==’ 比较基本类型字段,使用Objects.equals()比较对象字段。如果所有的字段都匹配,就返回true,否则返回false。
    return field1 == other.field1 && Objects.equals(field2, other.field2) && ...;
    如果在子类中重新定义equals,就要在其中包含一个super.equals(other)调用。

你可能感兴趣的:(Java基础知识,java)