第八条:覆盖equals时请遵守通用约定
2016-04-21 11:05
162 查看
覆盖equals方法可能会导致错误,避免这类问题就是不覆盖equals方法,这样情况下,类的每个实例都与它自身相等。满足以下任何一个条件,都是期望的结果:
类的每个实例本质上都是唯一的。
不关心类是否提供了“逻辑相等(logical equality)”的测试功能。
超类已经覆盖了equals,从超类继承过来的行为对于子类也是合适的。
类是私有的或是包级私有的,可以确定它的equals方法永远不会被调用。在这种情况下,无疑是应该覆盖equals方法的,以防它被意外调用
如果类具有自己特有的“逻辑相等”概念(不同于对象等同的概念),而且超类还没有覆盖equals以实现期望的行为,这时我们就需要覆盖equals方法。这通常是“值类”的情形,例如Integer或者Date。
用equals方法比较值对象的引用时,我们希望知道它们在逻辑上是否相等,不想了解它们是否指向同一个对象,这就需要覆盖equals方法,这样也会使得这个类的实例可以被用作映射表(map)的键,或者集合(set)的元素。
自反性(reflexive)。对于任何非null的引用值x,x.equals(x)必须返回true。
对称性(symmetric)。对于任何非null的引用值x和y,当且仅当y.equal(x)返回true时,x.equals(y)必须返回true。
传递性(transitive)。对于任何非null的引用值x、y和z,如果x.equals(y)返回true,并且y.equals(z)也返回true,那么x.equals(z)也必须返回true。
一致性(consistent)。对于任何非null的引用值x和y,只要equals的比较操作在对象中所用的信息没有修改,多次调用x.equals(y)就会一致地返回true,或者一致地返回false。
对于任何非null的引用值x,x.equals(null)必须返回false。
没有哪个类是孤立的,一个类的实例通常会被频繁地传递给另一个类的实例,有许多类,包括所有的集合类在内,都依赖于传递给它们的对象是否遵守了equals约定。
自反性(reflexivity):仅仅说明对象必须等于其自身,违背这一条,把该类实例添加到集合中,contains方法返回false。
对称性(symmetry):任何两个对象对于“它们是否相等”的问题都必须保持一致。
传递性(transitivity):如果一个对象等于第二个对象,并且第二个对象又等于第三个对象,则第一个对象一定等于第三个对象。
注意:等价关系可能存在一个基本问题:无法在扩展可实例化的类的同时,即增加新的组件,同时又保留equals约定,除非愿意放弃面向对象的抽象所带来的优势。可以采用复合的形式。
一致性(consistency):如果两个对象相等,他们就必须始终保持相等,除非他们被修改了。
非空性(Non-nullity):所有对象必须不等于null。
结合所有这些要求,得出以下实现高质量的equals方法的诀窍:
使用==操作符检查“参数是否为这个对象的引用”。
使用instanceof操作检查“参数是否为正确类型”。“正确类型”指equals方法所在的那个类,有些时候值该类所实现的某个接口。
把参数转换成正确的类型。
对于该类中的每个“关键(significant)”域,检查参数中的域是否与该对象中对应的域相匹配。对于既不是float也不是double类型的基本数据,可以使用 == 操作符比较;对于对象引用域,可以递归地调用equals方法;对于double域,则使用Double.compare;对于float域,则使用Float.compare方法。有些对象引用域包含null可能是合法的,为了避免可能导致NullPointException异常,则可以使用下面的习惯语法来比较这样的域:
如果field和o.filed通常是相同的对象引用,可以这么做:
5. 当编写完equals方法之后,应该注意三个问题:它是否对称的、传递的、一致的?
其他
覆盖equals时总要覆盖hashCode。
不要企图让equals方法过于智能。
不要将equals声明中的Object对象替换为其他的类型。
类的每个实例本质上都是唯一的。
不关心类是否提供了“逻辑相等(logical equality)”的测试功能。
超类已经覆盖了equals,从超类继承过来的行为对于子类也是合适的。
类是私有的或是包级私有的,可以确定它的equals方法永远不会被调用。在这种情况下,无疑是应该覆盖equals方法的,以防它被意外调用
如果类具有自己特有的“逻辑相等”概念(不同于对象等同的概念),而且超类还没有覆盖equals以实现期望的行为,这时我们就需要覆盖equals方法。这通常是“值类”的情形,例如Integer或者Date。
用equals方法比较值对象的引用时,我们希望知道它们在逻辑上是否相等,不想了解它们是否指向同一个对象,这就需要覆盖equals方法,这样也会使得这个类的实例可以被用作映射表(map)的键,或者集合(set)的元素。
自反性(reflexive)。对于任何非null的引用值x,x.equals(x)必须返回true。
对称性(symmetric)。对于任何非null的引用值x和y,当且仅当y.equal(x)返回true时,x.equals(y)必须返回true。
传递性(transitive)。对于任何非null的引用值x、y和z,如果x.equals(y)返回true,并且y.equals(z)也返回true,那么x.equals(z)也必须返回true。
一致性(consistent)。对于任何非null的引用值x和y,只要equals的比较操作在对象中所用的信息没有修改,多次调用x.equals(y)就会一致地返回true,或者一致地返回false。
对于任何非null的引用值x,x.equals(null)必须返回false。
没有哪个类是孤立的,一个类的实例通常会被频繁地传递给另一个类的实例,有许多类,包括所有的集合类在内,都依赖于传递给它们的对象是否遵守了equals约定。
自反性(reflexivity):仅仅说明对象必须等于其自身,违背这一条,把该类实例添加到集合中,contains方法返回false。
对称性(symmetry):任何两个对象对于“它们是否相等”的问题都必须保持一致。
传递性(transitivity):如果一个对象等于第二个对象,并且第二个对象又等于第三个对象,则第一个对象一定等于第三个对象。
注意:等价关系可能存在一个基本问题:无法在扩展可实例化的类的同时,即增加新的组件,同时又保留equals约定,除非愿意放弃面向对象的抽象所带来的优势。可以采用复合的形式。
一致性(consistency):如果两个对象相等,他们就必须始终保持相等,除非他们被修改了。
非空性(Non-nullity):所有对象必须不等于null。
结合所有这些要求,得出以下实现高质量的equals方法的诀窍:
使用==操作符检查“参数是否为这个对象的引用”。
使用instanceof操作检查“参数是否为正确类型”。“正确类型”指equals方法所在的那个类,有些时候值该类所实现的某个接口。
把参数转换成正确的类型。
对于该类中的每个“关键(significant)”域,检查参数中的域是否与该对象中对应的域相匹配。对于既不是float也不是double类型的基本数据,可以使用 == 操作符比较;对于对象引用域,可以递归地调用equals方法;对于double域,则使用Double.compare;对于float域,则使用Float.compare方法。有些对象引用域包含null可能是合法的,为了避免可能导致NullPointException异常,则可以使用下面的习惯语法来比较这样的域:
(filed == null ? obj.filed == null : filed.equals(obj.filed))
如果field和o.filed通常是相同的对象引用,可以这么做:
(filed == obj.filed || (filed != null && filed.equals(obj.filed)))
5. 当编写完equals方法之后,应该注意三个问题:它是否对称的、传递的、一致的?
其他
覆盖equals时总要覆盖hashCode。
不要企图让equals方法过于智能。
不要将equals声明中的Object对象替换为其他的类型。
相关文章推荐
- Android自定义属性,format详解
- Android中的onWindowFocusChanged()方法详解
- [Magento SQL] 返回sku,price,specialPrice。排除可配置产品和已下架产品
- Gatling新一代压力测试工具,新一代服务器性能测试工具Gatling
- Selenium Python 解决 UnexpectedAlertPresentException
- bzoj 4540: [Hnoi2016]序列
- MVC,MVP,MVVM的比较
- 在android中使用Post方式提交数据
- 2016-04-13T16:00:00.000Z时间格式转换(时差)
- Java线程中断的本质深入理解
- 初识java算法
- php代码优化
- 二叉树
- nfl_suspensions
- 决策树C4.5
- 必读书籍
- c++第4次实验-项目一
- 第四次c++实验
- 三级数据库知识点
- 虚函数-构造函数-析构函数