首先看一下String的equals()函数的重写:
1 /**
2 * Compares this string to the specified object. The result is {@code
3 * true} if and only if the argument is not {@code null} and is a {@code
4 * String} object that represents the same sequence of characters as this
5 * object.
6 *
7 * @param anObject
8 * The object to compare this {@code String} against
9 *
10 * @return {@code true} if the given object represents a {@code String}
11 * equivalent to this string, {@code false} otherwise
12 *
13 * @see #compareTo(String)
14 * @see #equalsIgnoreCase(String)
15 */
16 public boolean equals(Object anObject) {
17 if (this == anObject) {
18 return true;
19 }
20 if (anObject instanceof String) {
21 String anotherString = (String)anObject;
22 int n = value.length;
23 if (n == anotherString.value.length) {
24 char v1[] = value;
25 char v2[] = anotherString.value;
26 int i = 0;
27 while (n-- != 0) {
28 if (v1[i] != v2[i])
29 return false;
30 i++;
31 }
32 return true;
33 }
34 }
35 return false;
36 }
分析一下上面equals()函数:
(1) 首先比较两个对象的引用是否相等,如果两个引用相等,那么两个对象必然相等。
(2) 其次判断anObject是否是String的一个实例 (instanceof关键字的作用是测试一个对象是否是一个类的实例),如果不是则返回false,如果是,则判断obObject的字符串内容是否和this相同:长度是否相等,内容是否相等。
ps: 使用instanceof关键字的问题:当类A是类B的子类时,使用instanceof关键字重写equals()函数就会不符合java语言规范要求equals()方法的“对称性”特性的要求。对称性(对于任何引用x,y,当且仅当y.equals(x)返回true, x.equals(y)也应该返回true)。
一般我们在设计一个类时,需要重写父类的equals方法,在重写这个方法时,需要按照以下几个规则设计:
(1) 自反性:对任意引用值x,x.equals(x)的返回值一定为true.
(2) 对称性:对于任何引用值x,y,当且仅当y.equals(x)返回值为true时,x.equals(y)的返回值一定为true;
(3) 传递性:如果x.equals(y)=true, y.equals(z)=true,则x.equals(z)=true
(4) 一致性:如果参与比较的对象没任何改变,则对象比较的结果也不应该有任何改变
(5) 非空性:任何非空的引用值X,x.equals(null)的返回值一定为false
那么如何编写一个完美的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)现在开始对所有需要比较的域进行比较 。使用==比较基本类型域,使用equals比较对象域。如果所有的域都匹配,就返回true,否则就返回flase。
return field1 == other.field1 && Objects.equals(field2,other.field2) && ...;
(7)如果在子类中重新定义equals,就要在其中包含调用 super.equals(other)
举例(摘自Java核心技术)
父类:
1 package equals;
2
3 import java.time.*;
4 import java.util.Objects;
5
6 public class Employee
7 {
8 private String name;
9 private double salary;
10 private LocalDate hireDay;
11
12 public Employee(String name, double salary, int year, int month, int day)
13 {
14 this.name = name;
15 this.salary = salary;
16 hireDay = LocalDate.of(year, month, day);
17 }
18
19 public String getName()
20 {
21 return name;
22 }
23
24 public double getSalary()
25 {
26 return salary;
27 }
28
29 public LocalDate getHireDay()
30 {
31 return hireDay;
32 }
33
34 public void raiseSalary(double byPercent)
35 {
36 double raise = salary * byPercent / 100;
37 salary += raise;
38 }
39
40 public boolean equals(Object otherObject)
41 {
42 // a quick test to see if the objects are identical
43 if (this == otherObject) return true;
44
45 // must return false if the explicit parameter is null
46 if (otherObject == null) return false;
47
48 // if the classes don't match, they can't be equal
49 if (getClass() != otherObject.getClass()) return false;
50
51 // now we know otherObject is a non-null Employee
52 Employee other = (Employee) otherObject;
53
54 // test whether the fields have identical values
55 return Objects.equals(name, other.name) && salary == other.salary && Objects.equals(hireDay, other.hireDay);
56 }
57
58 public int hashCode()
59 {
60 return Objects.hash(name, salary, hireDay);
61 }
62
63 public String toString()
64 {
65 return getClass().getName() + "[name=" + name + ",salary=" + salary + ",hireDay=" + hireDay
66 + "]";
67 }
68 }
子类:
1 package equals;
2
3 public class Manager extends Employee
4 {
5 private double bonus;
6
7 public Manager(String name, double salary, int year, int month, int day)
8 {
9 super(name, salary, year, month, day);
10 bonus = 0;
11 }
12
13 public double getSalary()
14 {
15 double baseSalary = super.getSalary();
16 return baseSalary + bonus;
17 }
18
19 public void setBonus(double bonus)
20 {
21 this.bonus = bonus;
22 }
23
24 public boolean equals(Object otherObject)
25 {
26 if (!super.equals(otherObject)) return false;
27 Manager other = (Manager) otherObject;
28 // super.equals checked that this and other belong to the same class
29 return bonus == other.bonus;
30 }
31
32 public int hashCode()
33 {
34 return super.hashCode() + 17 * new Double(bonus).hashCode();
35 }
36
37 public String toString()
38 {
39 return super.toString() + "[bonus=" + bonus + "]";
40 }
41 }
注意:如果重新定义equals方法,就必须重新定义hashCode方法,以便用户可以将对象插入到散列表中。
如果不太懂(4)到底说的是神马,请看以下摘抄的一篇博文的部分内容,对比instanceof和getClass()的区别,其实getClass会判断实例的类是否相等,而instanceof相等的条件,包括两种情况:(1) 类自身 instanceof 类自身;(2) 子类 instanceof 父类;
1 class A { }
2
3 class B extends A { }
4
5 Object o1 = new A();
6 Object o2 = new B();
7
8 o1 instanceof A => true
9 o1 instanceof B => false
10 o2 instanceof A => true // <================ HERE
11 o2 instanceof B => true // <span style="font-family: Arial, Helvetica, sans-serif;"><================ HERE </span>
12
13 o1.getClass().equals(A.class) => true
14 o1.getClass().equals(B.class) => false
15 o2.getClass().equals(A.class) => false // <===============HERE
16 o2.getClass().equals(B.class) => true <span style="font-family: Arial, Helvetica, sans-serif;"> // </span><span style="font-family: Arial, Helvetica, sans-serif;"><================ HERE </span>
17
18
19
20 getClass() will be useful when you want to make sure your instance is NOT a subclass of the class you are comparing with.