equals方法的重写

一、为什么equals()方法要重写?

判断两个对象在逻辑上是否相等,如根据类的成员变量来判断两个类的实例是否相等,而继承Object中的equals方法只能判断两个引用变量是否是同一个对象。这样我们往往需要重写equals()方法。

我们向一个没有重复对象的集合中添加元素时,集合中存放的往往是对象,我们需要先判断集合中是否存在已知对象,这样就必须重写equals方法。

二、怎样重写equals()方法?

  • 重写equals方法的要求:
    1.自反性:对于任何非空引用x,x.equals(x)应该返回true。
    2.对称性:对于任何引用x和y,如果x.equals(y)返回true,那么y.equals(x)也应该返回true。
    3.传递性:对于任何引用x、y和z,如果x.equals(y)返回true,y.equals(z)返回true,那么x.equals(z)也应该返回true。
    4.一致性:如果x和y引用的对象没有发生变化,那么反复调用x.equals(y)应该返回同样的结果。
    5.非空性:对于任意非空引用x,x.equals(null)应该返回false。

格式:

Java代码  

  1. public boolean equals(Object obj) {
  2. if(this == obj)
  3. return false;
  4. if(obj == null)
  5. return false;
  6. if(getClass() != obj.getClass() )
  7. return false;
  8. MyClass other = (MyClass)obj;
  9. if(str1 == null) {
  10. if(obj.str1 != null) {
  11. return false;
  12. }
  13. }else if (!str1.equals(other.str1) )
  14. return false;
  15. }
  16. if(var1 != other.var1)
  17. return false;
  18. return true;
  19. }

如果子类中增加了新特性,同时保留equals方法,这时比较复杂。

接下来我们通过实例来理解上面的约定。我们首先以一个简单的非可变的二维点类作为开始: 
public class Point{ 
  private final int x; 
  private final int y; 
  public Point(int x, int y){ 
    this.x = x; 
    this.y = y; 
  }

  public boolean equals(Object o){ 
    if(!(o instanceof Point)) 
      return false; 
    Point p = (Point)o; 
      return p.x == x && p.y == y; 
  }

}

假设你想要扩展这个类,为一个点增加颜色信息: 
public class ColorPoint extends Point{ 
  private Color color; 
  public ColorPoint(int x, int y, Color color){ 
    super(x, y); 
    this.color = color; 
  }

  //override equasl()

  public boolean equals(Object o){ 
    if(!(o instanceof ColorPoint)) 
     return false; 
    ColorPoint cp = (ColorPoint)o; 
    return super.equals(o) && cp.color==color; 
  } 
}

  我们重写了equals方法,只有当实参是另一个有色点,并且具有同样的位置和颜色的时候,它才返回true。可这个方法的问题在于,你在比较一个普通点和一个有色点,以及反过来的情形的时候,可能会得到不同的结果: 
public static void main(String[] args){ 
  Point p = new Point(1, 2); 
  ColorPoint cp = new ColorPoint(1, 2, Color.RED); 
  System.out.println(p.equals(cp)); 
  System.out.println(cp.eqauls(p)); 
}

运行结果: 
true   
false 
这样的结果显然违反了对称性,你可以做这样的尝试来修正这个问题:让ColorPoint.equals在进行“混合比较”的时候忽略颜色信息: 
public boolean equals(Object o){ 
  if(!(o instanceof Point)) 
    return false; 
  //如果o是一个普通点,就忽略颜色信息 
  if(!(o instanceof ColorPoint)) 
    return o.equals(this); 
  //如果o是一个有色点,就做完整的比较 
  ColorPoint cp = (ColorPoint)o; 
  return super.equals(o) && cp.color==color; 
}

这种方法的结果会怎样呢?让我们先来测试一下: 
public static void main(String[] args){ 
  ColorPoint p1 = new ColorPoint(1, 2, Color.RED); 
  Point p2 = new Point(1, 2); 
  ColorPoint p3 = new ColorPoint(1, 2, Color.BLUE); 
  System.out.println(p1.equals(p2)); 
  System.out.println(p2.equals(p1)); 
  System.out.println(p2.equals(p3)); 
  System.out.println(p1.eqauls(p3)); 
}

运行结果: 
true 
true 
true 
false

  这种方法确实提供了对称性,但是却牺牲了传递性(按照约定,p1.equals(p2)和p2.eqauals(p3)都返回true,p1.equals(p3)也应返回true)。要怎么解决呢?

事实上,这是面向对象语言中关于等价关系的一个基本问题。要想在扩展一个可实例化的类的同时,既要增加新的特征,同时还要保留equals约定,没有一个简单的办法可以做到这一点。新的解决办法就是不再让ColorPoint扩展Point,而是在ColorPoint中加入一个私有的Point域,以及一个公有的视图(view)方法: 
public class ColorPoint{ 
  private Point point; 
  private Color color; 
  public ColorPoint(int x, int y, Color color){ 
    point = new Point(x, y); 
    this.color = color; 
  }

  //返回一个与该有色点在同一位置上的普通Point对象 
  public Point asPoint(){ 
    return point; 
  }

  public boolean equals(Object o){ 
    if(o == this) 
     return true; 
    if(!(o instanceof ColorPoint)) 
     return false; 
    ColorPoint cp = (ColorPoint)o; 
    return cp.point.equals(point)&& 
             cp.color.equals(color);

  } 
}

  还有另外一个解决的办法就是把Point设计成一个抽象的类(abstract class),这样你就可以在该抽象类的子类中增加新的特征,而不会违反equals约定。因为抽象类无法创建类的实例,那么前面所述的种种问题都不会发生。

重写equals方法的要点: 
1. 使用==操作符检查“实参是否为指向对象的一个引用”。

2.判断实参是否为null
3. 使用instanceof操作符检查“实参是否为正确的类型”。 
4. 把实参转换到正确的类型。 
5. 对于该类中每一个“关键”域,检查实参中的域与当前对象中对应的域值是否匹 
  配。对于既不是float也不是double类型的基本类型的域,可以使用==操作符 
  进行比较;对于对象引用类型的域,可以递归地调用所引用的对象的equals方法; 
  对于float类型的域,先使用Float.floatToIntBits转换成int类型的值, 
  然后使用==操作符比较int类型的值;对于double类型的域,先使用 
  Double.doubleToLongBits转换成long类型的值,然后使用==操作符比较 
  long类型的值。 
6. 当你编写完成了equals方法之后,应该问自己三个问题:它是否是对称的、传 
  递的、一致的?(其他两个特性通常会自行满足)如果答案是否定的,那么请找到 
  这些特性未能满足的原因,再修改equals方法的代码。

时间: 2024-11-03 22:21:49

equals方法的重写的相关文章

为什么重写equals方法还要重写hashcode方法?

我们都知道Java语言是完全面向对象的,在java中,所有的对象都是继承于Object类.Ojbect类中有两个方法equals.hashCode,这两个方法都是用来比较两个对象是否相等的. 在未重写equals方法我们是继承了object的equals方法,那里的 equals是比较两个对象的内存地址,显然我们new了2个对象内存地址肯定不一样 对于值对象,==比较的是两个对象的值 对于引用对象,比较的是两个对象的地址 默认的equals方法同==,一般来说我们的对象都是引用对象,要重写equ

对Equals()方法的重写

//手动的实现equals()方法 //    重写Object类的equals(Object obj)方法,保证两个对象若属性值完全相等,则返回true     public boolean equals(Object obj){         if(this==obj){             return true;         }else if(obj instanceof Person){             Person p=(Person)obj;           

equals方法的重写代码实例

这篇文章主要是要记录一下equals的重写,下面上代码,代码里有足够的注释 /**  *   */ /**  * @author laishengfeng  * @2014-8-20  * @TODO equals方法的重写  */ public class Citizen { String id; // 身份证号 String name; // 名字 int age; // 年龄 String sex; // 性别 // 用构造方法对成员变量进行初始化 public Citizen(Strin

为什么系列之重写equals方法必须重写hasCode方法?

Object源代码及注释 equals是Object的公有方法,那么我们通常都会在自己的类中重写这个equals方法,同时必须重写hasCode方法,知道为什么重写equals方法必须重写hasCode方法呢? /** * Returns a hash code value for the object. This method is * supported for the benefit of hash tables such as those provided by * {@link jav

【java】克隆clone()方法和相等equals()方法的重写

1.为什么要重写clone()方法? Java中的浅度复制是不会把要复制的那个对象的引用对象重新开辟一个新的引用空间,当我们需要深度复制的时候,这个时候我们就要重写clone()方法. 2.equals()和clone()方法重载的示例 Hourse类: import java.util.Date; public class House implements Cloneable { private int id; private double area; private Date whenBul

为什么重写 equals 方法 必须重写 hashCode

自己学到这,就记录了下来,代码都是自己敲得,有不对的地方希望大神指点出来 如果你重写了equals,比如说是基于对象的内容实现的,而不重写 HashCode,那么很可能某两个对象明明是“相等”,而hashCode却不一样.那么就会返回 false.例如在 HashSet 集合中,不允许存在相同元素,但是当你 new 两个对象,对象内容相同时,在 HashSet 集合中就存在了相同的两个元素,如何解决这个问题?那就要重写 equals 方法,但是重写 equals 方法还不能解决问题,因为在 Ha

HashSet——add remove contains方法底层代码分析(hashCode equals 方法的重写)

引言:我们都知道HashSet这个类有add   remove   contains方法,但是我们要深刻理解到底是怎么判断它是否重复加入了,什么时候才移除,什么时候才算是包括????????? add()方法 首先我们看下这个代码 1 package com.xt.set; 2 3 import java.util.HashSet; 4 import java.util.Iterator; 5 import java.util.Set; 6 7 public class AddTest { 8

为什么重写equals方法必须重写hashCode?

如果两个对象调用equals返回相等,那么这两个对象调用hashCode方法必须返回相同的整数.如果不重写hashCode,会违反该原则.这个原则主要针对的是映射操作(map接口). 注意: 1. String重写了Object的equals,现在比较的是两个String所包含的char sequence是否相等. String s1 = new String("hello"); String s2 = new String("hello"); s1.equals(

java重写equals方法(重点讲解)

为什么equals()方法要重写? 判断两个对象在逻辑上是否相等,如根据类的成员变量来判断两个类的实例是否相等,而继承Object中的equals方法只能判断两个引用变量是否是同一个对象.这样我们往往需要重写equals()方法. 我们向一个没有重复对象的集合中添加元素时,集合中存放的往往是对象,我们需要先判断集合中是否存在已知对象,这样就必须重写equals方法. 怎样重写equals()方法? 重写equals方法的要求: 1.自反性:对于任何非空引用x,x.equals(x)应该返回tru