首先看一下 String 的 equals()函数的重写:
- /**
- * Compares this string to the specified object. The result is {@code
- * true} if and only if the argument is not {@code null} and is a {@code
- * String} object that represents the same sequence of characters as this
- * object.
- *
- * @param anObject
- * The object to compare this {@code String} against
- *
- * @return {@code true} if the given object represents a {@code String}
- * equivalent to this string, {@code false} otherwise
- *
- * @see #compareTo(String)
- * @see #equalsIgnoreCase(String)
- */
- public boolean equals(Object anObject) {
- if (this == anObject) {
- return true;
- }
- if (anObject instanceof String) {
- String anotherString = (String)anObject;
- int n = value.length;
- if (n == anotherString.value.length) {
- char v1[] = value;
- char v2[] = anotherString.value;
- int i = 0;
- while (n-- != 0) {
- if (v1[i] != v2[i])
- return false;
- i++;
- }
- return true;
- }
- }
- return false;
- }
分析一下上面 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 核心技术)
父类:
- package equals;
- import java.time.*;
- import java.util.Objects;
- public class Employee
- {
- private String name;
- private double salary;
- private LocalDate hireDay;
- public Employee(String name, double salary, int year, int month, int day)
- {
- this.name = name;
- this.salary = salary;
- hireDay = LocalDate.of(year, month, day);
- }
- public String getName()
- {
- return name;
- }
- public double getSalary()
- {
- return salary;
- }
- public LocalDate getHireDay()
- {
- return hireDay;
- }
- public void raiseSalary(double byPercent)
- {
- double raise = salary * byPercent / 100;
- salary += raise;
- }
- public boolean equals(Object otherObject)
- {
- // a quick test to see if the objects are identical
- if (this == otherObject) return true;
- // must return false if the explicit parameter is null
- if (otherObject == null) return false;
- // if the classes don't match, they can't be equal
- if (getClass() != otherObject.getClass()) return false;
- // now we know otherObject is a non-null Employee
- Employee other = (Employee) otherObject;
- // test whether the fields have identical values
- return Objects.equals(name, other.name) && salary == other.salary && Objects.equals(hireDay, other.hireDay);
- }
- public int hashCode()
- {
- return Objects.hash(name, salary, hireDay);
- }
- public String toString()
- {
- return getClass().getName() + "[name=" + name + ",salary=" + salary + ",hireDay=" + hireDay
- + "]";
- }
- }
子类:
- package equals;
- public class Manager extends Employee
- {
- private double bonus;
- public Manager(String name, double salary, int year, int month, int day)
- {
- super(name, salary, year, month, day);
- bonus = 0;
- }
- public double getSalary()
- {
- double baseSalary = super.getSalary();
- return baseSalary + bonus;
- }
- public void setBonus(double bonus)
- {
- this.bonus = bonus;
- }
- public boolean equals(Object otherObject)
- {
- if (!super.equals(otherObject)) return false;
- Manager other = (Manager) otherObject;
- // super.equals checked that this and other belong to the same class
- return bonus == other.bonus;
- }
- public int hashCode()
- {
- return super.hashCode() + 17 * new Double(bonus).hashCode();
- }
- public String toString()
- {
- return super.toString() + "[bonus=" + bonus + "]";
- }
- }
注意: 如果重新定义 equals 方法, 就必须重新定义 hashCode 方法, 以便用户可以将对象插入到散列表中.
如果不太懂 (4) 到底说的是神马, 请看以下摘抄的一篇博文的部分内容, 对比 instanceof 和 getClass()的区别, 其实 getClass 会判断实例的类是否相等, 而 instanceof 相等的条件, 包括两种情况:(1) 类自身 instanceof 类自身;(2) 子类 instanceof 父类;
- class A { }
- class B extends A { }
- Object o1 = new A();
- Object o2 = new B();
- o1 instanceof A => true
- o1 instanceof B => false
- o2 instanceof A => true // <================ HERE
- o2 instanceof B => true // <span style="font-family: Arial, Helvetica, sans-serif;"><================ HERE </span>
- o1.getClass().equals(A.class) => true
- o1.getClass().equals(B.class) => false
- o2.getClass().equals(A.class) => false // <===============HERE
- 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>
- getClass() will be useful when you want to make sure your instance is NOT a subclass of the class you are comparing with.
参考博客: https://blog.csdn.net/wu_cai_/article/details/51989033
来源: http://www.bubuko.com/infodetail-2862787.html