网站首页 > 精选教程 正文
原文
5.2.3相等测试与继承
如果隐式和显式的参数不属于同一个类,equals 方法将如何处理呢?这是一个很有争议的问题。在前面的例子中,如果发现类不能完全匹配,equals 方法就返回 false。但是,许多程序员喜欢使用 instanceof 进行检测:
if (!(otherObject instanceof Employee))
return false;
这样就允许 otherObject 属于一个子类。但是这种方法可能会招致一些麻烦。下面会解释原因。Java语言规范要求 equas 方法具有下述性质:
1. 自反性:对于任何非 null 引用 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.对于任意非 null引用x,x.equals(null) 应该返回 false。
这些规则当然很合理。你肯定不希望类库实现者在查找数据结构中的一个元素时纠结调
用 x.equals(y) 还是调用 y.equals(x)。
不过,就对称性规则来说,当参数属于不同的类时会有一些微妙的结果。请看下面这个
调用:
e.equals(m)
这里的e是一个Employee对象,m是一个Manager 对象,并且这两个对象恰好有相同的姓名薪水和雇用日期。如果在 Employee.equals 使用 instanceof 进行检测,则这个调用将返回 true不过这意味着,如果反过来调用:
m.equals(e)也需要返回 true。对称性规则不允许这个方法调用返回 false 或者抛出异常。
这就使得 Manager类陷人困境。这个类的 equals 方法必须愿意将自己与任何一个 Employee对象进行比较,而不考虑经理特有的那部分信息!猛然间这让人感觉 instanceof 测试并不是那么好。
有些作者认为 getClass 检测是有问题的,因为它违反了替换原则。有一个经常提到的例子,就是AbstractSet 类的 equals 方法,它将检测两个集合是否有相同的元素。AbstractSet类有两个具体子类:TreeSet 和 HashSet。它们分别使用不同的算法查找集合元素。你肯定希望能够比较任意的两个集合,而不论它们如何实现。
不过,这个集合例子非常特殊,最好将 AbstractSet.equals 声明为 final,因为不应该重新定义集合的相等语义(但事实上,这个方法并没有声明为 final。这是为了让子类实现更高效的算法来完成相等性检测)。
就现在来看,有两种完全不同的情形:
- 如果子类可能有自己的相等性概念,则对称性需求强制使用 getclass 检测。
- 如果由超类决定相等性概念,那么可以使用 instanceof 检测,这样不同子类的对象也可能相等。
在员工和经理的例子中,只要对应的字段相等,就认为两个对象相等。如果两个 Managel对象的姓名、薪水和雇用日期均相等,而奖金不相等,就认为它们不同,因此,我们要使用getClass 检测。
但是,假设使用员工的ID 来检测相等性,并且这个相等性概念适用于所有的子类,就可以使用 instanceof检测,而且应该将 Employee.equals 声明为 final。
注释 : 在标准Java库中包含 150多个equals 方法的实现,包括使用 instanceof 检测调用getClass、捕获 ClassCastException 或者什么也不做等各种不同做法。可以查看 java.sql.Timestamp类的API文档,在这里实现人员不无尴尬地指出,他们让自己陷入了困境。Timestamp类继承自java.util.Date,而后者的 equals 方法使用了一个instanceof检测,这样一来就无法覆盖 equals,使之同时做到对称且正确。
下面给出编写完美equals 方法的技巧:
- 将显式参数命名为 otherObject,稍后需要将它强制转换成另一个名为 other 的变量
- 检测 this 与 otherobject 是否相同:
- if (this = otherObject) return true;
这条语句只是一个优化。实际上,这种情况很常见,因为检查同一性要比逐个比较字段开销小。
3.检测otherObject 是否为 null,如果为 null,则返回 false。这个检测是必要的
if (otherObject == null) return false;
4.比较 this 与 otherObject 的类。如果 equals 的语义可以在子类中改变,就使用 getClass 检测:
if (getClass() != other0bject.getClass())
?return false;
ClassName other = (ClassName) otherObject;
如果所有的子类都有相同的相等性语义,则可以使用 instanceof 检测:
if (!(otherObject instanceof ClassName other)) return false;
注意,如果 instanceof 检测成功,它会把 other 设置为 otherobject。不再需要强制类型转换。
5. 现在根据相等性概念的要求来比较字段。使用== 比较基本类型字段,使用 Objects.equals 比较对象字段。如果所有的字段都匹配,就返回 true; 否则,返回 false。
?return field1 = other.field1
? ?&&Objects.equals(field2,other.field2)
? ?&&...
如果在子类中重新定义equals,就要在其中包含一个 super.equals(other) 调用。
提示:对于数组类型的字段,可以使用静态的 Arrays.equals 方法检查相应的数组元素是否相等。对于多维数组,可以使用Arrays.deepEquals 方法。
警告:下面是实现 equals 方法时常见的一个错误。你能找到其中的问题吗?
public class Employee{
public boolean equals(Employee other){
return other != null
&& getClass() == other.getClass()
&& Objects.equals(name, other.name)
&& salany == other.salary
&& Objects.equals(hireDay,other.hireDay);
}
}
这个方法声明的显式参数类型是 Employee。因此,它没有覆盖 Object 类的 equals 方
法,而是定义了一个完全无关的方法。
为了避免发生这种错误,可以使用 @Override 标记要覆盖超类方法的那些子类方法:
@Override public boolean equals(0bject other)
如果犯了错误,没有覆盖方法而是在定义一个新方法,编译器就会报告一个错误,例如,假设将下面的声明添加到 Employee 类中:
@Override public boolean equals(Employee other)
就会报告一个错误,因为这个方法并不会覆盖超类 bject 中的任何方法
API : java.util.Arrays JDK1.2
static boolean equals(xx[] a, xx[] b) JDK5
如果两个数组长度相同,并且对应位置上的元素也相同,则返回 true。数组的元素类型x 可以是 Object、int、long、short、char、 byte、 boolean、 float 或 double。
API : java.util.0bjects JDK 7
static boolean equals(Object a, Object b)
如果a和b都为null,则返回 true; 如果只有其中之一为 null,则返回 false; 否
返回a.equals(b)。
- 上一篇: Java入门书单
- 下一篇: 深入浅出Rust异步编程之Tokio
猜你喜欢
- 2024-11-28 java 核心技术-12版 卷Ⅰ- 5.2.4 hashCode方法
- 2024-11-28 Java入门书单
- 2024-11-28 java 核心技术-12版 卷Ⅰ- 4.3.7 隐式参数与显式参数
- 2024-11-28 全网最完整的免费java教程讲义系列(四)——java的流程控制
- 2024-11-28 java 核心技术-12版 卷Ⅰ- 4.6 对象构造 4.6.1重载
- 2024-11-28 java 核心技术-12版 卷Ⅰ- 3.10 数组
- 2024-11-28 java 核心技术-12版 卷Ⅰ- 4.3 自定义类
- 2024-11-28 2019年Java核心技术我整理了200多页pdf,今天分享给你
- 2024-11-28 java 核心技术12版卷1 - 1.3 Java applet 与 Internet
- 2024-11-28 java 核心技术-12版 卷Ⅰ- 4.2 使用预定义类
你 发表评论:
欢迎- 最近发表
- 标签列表
-
- nginx反向代理 (57)
- nginx日志 (56)
- nginx限制ip访问 (62)
- mac安装nginx (55)
- java和mysql (59)
- java中final (62)
- win10安装java (72)
- java启动参数 (64)
- java链表反转 (64)
- 字符串反转java (72)
- java逻辑运算符 (59)
- java 请求url (65)
- java信号量 (57)
- java定义枚举 (59)
- java字符串压缩 (56)
- java中的反射 (59)
- java 三维数组 (55)
- java插入排序 (68)
- java线程的状态 (62)
- java异步调用 (55)
- java中的异常处理 (62)
- java锁机制 (54)
- java静态内部类 (55)
- java怎么添加图片 (60)
- java 权限框架 (55)
本文暂时没有评论,来添加一个吧(●'◡'●)