重写java object类的equals方法

覆盖equals方法请遵守约定

什么情况下要覆盖equals方法

容易违反的对称性

不易察觉的传递性

覆盖equals请遵守通用约定

似乎覆盖equals方法看起来似乎是一件平常甚至极其简单的事情,

但是有许多覆盖方式会导致错误,并且会表现出超出预期的行为,

而有可能数小时也无法找到错误的位置。(比如说把参数改成了非object类型)

1. 类的每一个实例在本质上都是唯一的

( 从内存的角度来讲是这样的),对于代表活动而不是值(value)的类来说更是如此,

例如thread。

object提供equals的实现对于这些类来说是正确的行为

2. 类没有必要提供“逻辑相等”的测试功能

3.超类已经覆盖了equals方法,超类的行为对于子类来说同样也是合适的

4.类是私有的或者是包级私有的,可以确定它的equals方法永远不会被外界调用

如果非常想规避风险,可以覆盖equals方法,

来确保来自object或者超类的方法永远不会被意外调用。

那么什么时候应该覆盖equals方法

如果类具有自己特有的“逻辑相等”概念(不同于对象等同的概念)

而且超类没有覆盖equals方法。这通常属于"值类"(value class)的情形

例如 一个圆 circle类,内有一个私有的成员变量radius半径

可以认为,radius相等代表了两个实例在逻辑上相等(或许可以再加上坐标)

再看string类,程序员在利用equals方法比较值对象的引用时,

更希望知道它们逻辑上是否相等,而不希望知道它们到底是不是同一个对象

为满足要求,不仅必须覆盖equals方法,

而且这样做也使得这个类的实例

可以被用作映射表 (map) 的键 (key) ,或者集合set的元素,

使其表现出符合预期的行为

注意:有一种“值类”不需要覆盖equals方法

即实例受控,甚至于单例模式,

确保每个实例的“值”至多只存在一个对象,甚至仅能存在一个实例

(好像太严格了,不过只能存在一个对象有什么可比的呢,就像客户端只能有一个连接服务器的socket类实例一样)

覆盖equals时请遵守通用约定

自反性,对称性以及传递性是最基础的约定

x.equals(x) = x.equals(x) (好像很傻)

x.equals(y) = y.equals(x)(这也是最容易出现问题的地方)

x.equals(y) = y.equals(z) 那么x.equals(z) == true

一致性:

对于任何非null引用值x和y,只要equals方法的比较操作在对象中引用的信息没有被修该,多次调用x.equals(y)返回的结果一致

对于任何非null的引用值x,x.equals(null)必须返回false

下面是一个不区分大小写字符串类的定义(注意 是反例!!违反了对称性)

public class caseinsensitivestring {

private final string s;//存有不可变字符串s

public caseinsensitivestring(string s) {

this.s = s;

}

@override //重写equals方法,请注意参数为object

public boolean equals(object o) {

if (o instanceof caseinsensitivestring)//判断传入的是同类型的参数

{

return s.equalsignorecase(((caseinsensitivestring) o).s);

}

if (o instanceof string) {

return s.equalsignorecase(((string) o));

}

return false;

}

}

注意:equals方法中的参数object o ,一定不要定义成其他类型!!

在绝大多数情况都要加上一句instanceof 来给o 赋予类型!!!

这个类的equals方法看起来设计的很有想法,不仅和自身比较,也希望和string类型进行比较。

//也确实看起来实现了功能

public static void main(string[] args)

{

//不区分大小写的字符串point

caseinsensitivestring cis = new caseinsensitivestring("point");

string s = "point";

system.out.printf(cis.equals(s)+"");//true

}

但是 s.equals(cis)却返回了false

//s.equals(cis)却返回了false

public static void main(string[] args)

{

caseinsensitivestring cis = new caseinsensitivestring("point");

string s = "point";

system.out.printf(cis.equals(s)+"");//true

system.out.printf(s.equals(cis)+"");//false

}

虽然caseinsensitivestring类中的equals方法知道普通的字符串对象

但是string类中的equals方法并不知道不区分大小写的字符串,

导致了超出预期的错误

显然违反了对称性。

一旦违反了equals约定,当其他对象面对你的对象是,你完全不知道这些对象的行为会怎样

为了解决这个问题,只需要把企图和string互相操作的代码删除就可以了

public class caseinsensitivestring {

private final string s;//存有不可变字符串s

public caseinsensitivestring(string s) {

this.s = s;

}

@override //重写equals方法,请注意参数为object

public boolean equals(object o) {

if (o instanceof caseinsensitivestring)//判断传入的是同类型的参数

{

return s.equalsignorecase(((caseinsensitivestring) o).s);

}

/*删除掉的与string互操作的代码*/

return false;

}

}

考虑这样一种情况

有一个坐标类,内有成员变量x和y

并提供equals方法

public class point {

private final int x;

private final int y;

public point(int x,int y)

{

this.x=x;

this.y=y;

}

@override

public boolean equals(object o )

{

if(o instanceof point)

{

point p = (point)o;

return (this.x == p.x && this.y==p.y);

}

return false;

}

}

好像简单到不能再简单的定义

那么扩展(继承)一下,为这个点添加颜色信息

public class colorpoint extends point{

private final color color;

public colorpoint(int x,int y,color color)

{

super(x,y);

this.color=color;

}

}

现在考虑一下

equals方法是什么样的呢?

如果子类coloepoint完全不提供equals方法,而是直接使用父类的equals方法

在equals做比较的时候颜色信息就被忽略掉了。

虽然这么做不会违反equals约定

但显然是无法接受的。

那么就重写一个equals方法。只有当坐标点x,y相同且颜色也相同时

equals才返回true

public class colorpoint extends point{

private final color color;

public colorpoint(int x,int y,color color)

{

super(x,y);

this.color=color;

}

@override

public boolean equals(object o)

{

if(o instanceof colorpoint)

{

return (super.equals(o)&& this.color==

( (colorpoint) o ).color);//使用父类的equals方法

}

return false;

}

}

这个方法的问题在于,比较普通点(没有颜色的point实例)和

有色点(colorpoint实例)比较,以及相反的情况时可能会得到不同的结果

前一种忽略的颜色信息,而后一种总时返回false(因为参数类型不正确)

public static void main(string[] args)

{

point p = new point(1,2);

colorpoint cp = new colorpoint(1,2,color.red);

system.out.printf(p.equals(cp)+"");//true

system.out.printf(cp.equals(p)+"");//false

}

可以尝试以colorpoint.equals在进行“混合颜色比较时”忽略颜色信息

但这样做确实提供了对称性,但却牺牲了传递性。

那该怎么解决???

事实上,这是面向对象语言中关于等价关系的一个基本问题。

我们无法在扩展可实例化的类的同时,既增加新的组件,同时又保留equals约定虽然没有一种令人满意的办法既可以扩展可实例化的类,又增加组件,但还有一种不错的方法。复合优先于继承

我们不再让colorpoint继承point,

而是在colorpoint类中添加一个私有的point域

以及一个公有的视图(view)方法

此(view)方法返回一个与该色点处在相同位置的普通point对象。

public class colorpoint{

private final color color;

private final point point;

public colorpoint(int x,int y,color color)

{

point = new point(x,y);

this.color=color;

}

/*view*/

public point aspoint()

{

return point;

}

@override

public boolean equals(object o)

{

if(o instanceof colorpoint)

{

colorpoint cp =(colorpoint)o;

return this.point.equals(cp.point)&&

this.color.equals(cp.color);//调用point以及color类的equals方法

}

//那么有色点与无色点会被判断为不相同而返回false

return false;

}

}

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持萬仟网。

希望与广大网友互动??

点此进行留言吧!

java 重写equals的要点_浅谈java 重写equals方法的种种坑相关推荐

  1. java中单例的应用_浅谈Java中单例模式的几种应用

    目录 浅谈Java中单例模式的几种应用 第一种:懒汉式 第二种:饿汉式 第三种:双重检索式 第四种:注册登记式 第五种:内部类形式 浅谈Java中单例模式的几种应用 日常开发中,为了提高我们系统中对象 ...

  2. java的向下转型_浅谈Java向下转型的意义

    一开始学习 Java 时不重视向下转型.一直搞不清楚向下转型的意义和用途,不清楚其实就是不会,那开发的过程肯定也想不到用向下转型. 其实向上转型和向下转型都是很重要的,可能我们平时见向上转型多一点,向 ...

  3. java字符串常量存哪里_浅谈JAVA中字符串常量的储存位置

    在讲述这些之前我们需要一些预备知识: Java的内存结构我们可以通过两个方面去看待它. 从该角度看的话Java内存结构包含以下部分:该部分内容可以结合:JVM简介(更加详细深入的介绍) 1.栈区:由编 ...

  4. php和java的区别菜鸟教程_浅谈Java和PHP的主要区别

    当谈到PHP与Java的差异性问题时,更多的是回答初学者的一些疑问.对于刚接触IT的同学来说,他们需要做好对未来职业的选择.所以是选择PHP还是选择Java更有利于自身的技术特点和发展前景.所以在解决 ...

  5. java类的命名规范_浅谈Java中的命名规范

    现代软件架构的复杂性需要协同开发完成,如何高效地协同呢? 答案是:制定一整套统一的规范. 无规矩不成方圆,无规范难以协同,比如,制订交通法规表面上是要限制行车权,实际上是保障公众的人身安全,试想如果没 ...

  6. java接口是干啥_浅谈Java接口

    接口(英文:Interface)是Java中非常重要的内容,初学的时候可能感受不深,但是在做项目的时候,对面向接口编程的运用就变得尤为重要,不过这是后话了.现在先讨论假如是刚刚接触接口这个概念,该怎么 ...

  7. java四种内部类区别_浅谈Java中的四种内部类

    如果你看过一些JDK和框架源码的话,就经常会发现一般在类的定义中,都会再定义一些其他的类,这些类也同样会被编译成字节码文件,这样的类就被叫做 内部类 ,按照一般的分法,大致可以分为以下四类: 成员内部 ...

  8. scale和java比较_浅谈java中BigDecimal的equals与compareTo的区别

    这两天在处理支付金额校验的时候出现了点问题,有个金额比较我用了BigDecimal的equals方法来比较两个金额是否相等,结果导致金额比较出现错误(比如3.0与3.00的比较等). [注:以下所讲都 ...

  9. java对象头_浅谈java对象结构 对象头 Markword

    概述 对象实例由对象头.实例数据组成,其中对象头包括markword和类型指针,如果是数组,还包括数组长度; | 类型 | 32位JVM | 64位JVM| | ------ ---- | ----- ...

  10. java 多线程同步_浅谈Java多线程(状态、同步等)

    Java多线程是Java程序员必须掌握的基本的知识点,这块知识点比较复杂,知识点也比较多,今天我们一一来聊下Java多线程,系统的整理下这部分内容. 一.Java中线程创建的三种方式: 1.通过继承T ...

最新文章

  1. Java项目:医院预约挂号系统(java+SpringBoot+Maven+Vue+mysql)
  2. html5小游戏Untangle
  3. CCNA课堂练习一:路由器链路备份功能
  4. Python编程基础:第四十七节 抽象类Abstract Classes
  5. 动态规划之----最长公共子序列
  6. 万能数据库查询分析器使用技巧之(十四)
  7. 关于oracleblob字段的用到
  8. python rgb led控件_Raspberry Pi-用树莓派实现RGB LED的颜色控制——Python版本-电路城论坛 - 电子工程师学习交流园地...
  9. 你不知道你不懂javascript
  10. error while loading shared libraries: xxx.so.x错误
  11. Android系统性能优化(44)---全面详细的内存优化指南
  12. 某游戏服务运维架构进化史(上云方案)
  13. Zabbix(六) zabbix主动模式监控
  14. Js toString()方法笔记
  15. openairinterface 中手动安装编译 UHD, Ubuntu 16.04
  16. unity中的rigibody 和 collider 讲解
  17. php mysql 手机归属地_PHP 手机号码归属地查询代码 (API 接口 / mysql)
  18. C语言二叉树非递归遍历详解,C语言实现二叉树的递归遍历和非递归遍历
  19. WORD无法复制文件:无法读源文件或磁盘
  20. 单片机c语言计算器,基于STC89C52单片机的计算器的设计.doc

热门文章

  1. jmeter常用功能
  2. Visual Studio 2017 编译Clang
  3. 四、kafka整体架构
  4. Linux下查看系统版本号信息的方法(转)
  5. Demo之JavaEE的Web中数据分页显示
  6. HCIE Secuirty AC概述 备考笔记(幕布)
  7. Docker详解(十四)——Docker网络类型详解
  8. Linux PXE详解
  9. js中的object
  10. dnSpy - 一款 .NET 程序逆向工具