`

覆盖Object的equals方法时准守的通用约定

    博客分类:
  • java
阅读更多

尽管Object是一个具体类,但是设计它主要是为了扩展。它所有的非final方法(equals、hashCode、toString、clone和finalize)都有明确的通用约定,因为它们被设计成是要被覆盖的。

任何一个类,它在覆盖这些方法的时候,都有责任遵守这些通用约定;如果不能做到这一点,其他依赖于这些约定的类(例如:HashMap和HashSet)就无法结合该类一起正常工作。

 

有一种"值类"不需要覆盖equals方法,即实例受控确保"每个值至多只存在一个对象"的类。枚举类型就属于这种类。

对于这样的类而言,逻辑相同与对象等同是一回事,因此Object的equals方法等同于逻辑意义上的equals方法。

 

如果类具有自己特有的“逻辑相等”概念(不同于对象等同的概念),而且超类还没有覆盖equals以实现期望的行为,这时我们需要覆盖equals方法。

 

这通常属于“值类(value class)”的情形。值类仅仅是一个表示值的类,例如Integer或者Date。

程序员在利用equals方法来比较对象的引用时,希望知道它们在逻辑上是否相等,而不是想了解它们是否指向同一个对象。

为了满足程序员的要求,不仅必须重写equals方法,而且这样做也使得这个类的实例可以被用作映射表的键,或者集合的元素,使映射和集合表现出预期的行为。

 

 

什么时候应该覆盖Object.equals

如果类具有自己特有的”逻辑相等“概念(不同于对象等同的概念),而且超类还没有覆盖equals以实现期望的行为。

 

在覆盖equals方法的时候,你必须要遵守它的通用约定(自反性、对称性、传递性、一致性)。

为什么一定要遵守通用约定,因为其他的程序设计都认为你遵循了通用约定,例如其他程序认为:x.equals(y)跟y.equals(x)是一样的。

如果你没有遵循,其他的程序代码就会有问题。

list.contains(s);

 

在覆盖equals方法的时候,必须遵守它的通用约定。

equals方法实现了等价关系

自反性:对于任何非null的引用值x,x.equals(x)必须返回true。

对称性:对于任何非null的引用值x和y,当且仅当y.equals(x)返回true时,x.equals(y)必须返回true。

传递性:对于任何非null的引用值x、y和z,如果x.equals(y)返回true,并且y.equals(z)也返回true,那么x.equals(z)也必须返回true。

一致性:对于任何非null的引用值x和y,只要equals的比较操作在对象中所用的信息没有被修改,多次调用x.equals(y)就会一致的返回true/false。

非空性:对于任何非null的引用值x,x.equals(null)必须返回false。(抛异常也不行)

 

对称性违反的例子

package com.ez.impl;
/**
 * 违反对称性
 */
public class CaseIgnoreString {
    private final String s;
    
    public CaseIgnoreString(String s){
        if(s==null)
            throw new NullPointerException();
        this.s=s;
    }
    
    @Override
    public boolean equals(Object obj) {
        if(obj instanceof CaseIgnoreString){
            return s.equalsIgnoreCase(((CaseIgnoreString)obj).s);
        }else if(obj instanceof String){
            return s.equalsIgnoreCase((String)obj);
        }
        return false;
    }
    
    public static void main(String[] args) {
        CaseIgnoreString cs=new CaseIgnoreString("EZbcw");
        String str="ezbcw";
        System.out.println(cs.equals(str));
        System.out.println(str.equals(cs));
    }
}

 

假如你把不区分大小写的字符串对象放到一个集合中,此时list.contains(s)会返回什么结果呢?没人知道,在Sun的当前实现中,它碰巧返回false。

public boolean contains(Object o) {
        return indexOf(o) >= 0;
    }

    public int indexOf(Object o) {
        if (o == null) {
            for (int i = 0; i < size; i++)
                if (elementData[i]==null)
                    return i;
        } else {
            for (int i = 0; i < size; i++)
                if (o.equals(elementData[i]))
                    return i;
        }
        return -1;
    }

 

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

 

传递性违反的例子

package com.ez.impl;
/**
 * 长方形
 */
public class Oblong {
    private final int width;
    private final int length;
    
    public Oblong(int width,int length){
        this.width=width;
        this.length=length;
    }
    
    @Override
    public boolean equals(Object obj) {
        if(!(obj instanceof Oblong)){
            return false;
        }
        Oblong oblong=(Oblong)obj;
        return oblong.width==width&&oblong.length==length;
    }
}

 

/**
 * 长方体
 */
public class Cuboid extends Oblong{

    private int height;
    
    public Cuboid(int width,int length,int height){
        super(width, length);
        this.height=height;
    }
    
    @Override
    public boolean equals(Object obj) {
        if(!(obj instanceof Oblong)){
            return false;   //不是长方形直接返回false
        }
        else if(!(obj instanceof Cuboid)){
            return obj.equals(this);    //长方形比较长和宽
        }
        return super.equals(obj)&&((Cuboid)obj).height==height; //长方体比较长宽高
    }
    
}

 

 

在Java平台类库中,有一些类扩展了可实例化的类,并添加了新的值组件。

例如,java.sql.Timestamp对java.util.Date进行了扩展,并增加了nanoseconds域。

Timestamp的equals实现确实违反了对称性,如果Timestamp和Date对象被用于一个集合中,或者以其他方式被混合在一起,则会引起不正确的行为。

Timestamp类有一个免责声明,告诫程序员不要混合使用Date和Timestamp对象。

 

覆盖equals时总要覆盖hashCode

 

如果两个对象根据equals(Object)方法比较是相等的,那么调用这两个对象中任意一个对象的hashCode方法都必须产生同样的整数结果。

 

如果两个对象根据equals(Object)方法比较是不相等的,那么调用这两个对象中任意一个对象的hashCode方法,则不一定产生不同的整数结果。

 

例子:通过List的removeAll方法,重写类的equals和hashcode方法,来实现

package com.ez.test.mytests;

import java.util.ArrayList;
import java.util.List;

/**
 * 逻辑上认为User的id相同,就认为是同一个人,因为一个用户可以取多个名字,但是id是唯一的。
 * 重写equals为了list去重-apps.removeAll(apps1),重写equals必须同时重写hashcode。
 * 使用Eclipse自动生成的hashCode
 */
public class User 
{
	private int id;
	private String name;
    
	public static void main( String[] args )
    {
        List<User> apps=new ArrayList<User>();
        User app=new User();
        app.setId(1);
        app.setName("张山");
        User app1=new User();
        app1.setId(2);
        app1.setName("李四");
        User app2=new User();
        app2.setId(3);
        app2.setName("王五");
        apps.add(app);
        apps.add(app1);
        apps.add(app2);
        
        List<User> apps1=new ArrayList<User>();
        User app3=new User();
        app3.setId(1);
        app3.setName("孙六");
        User app4=new User();
        app4.setId(2);
        app4.setName("陈七");
        apps1.add(app3);
        apps1.add(app4);
        
        apps.removeAll(apps1);
        System.out.println("apps size="+apps.size()+", id="+apps.get(0).getId()+", name="+apps.get(0).getName());
    }
    
    
	public int getId() {
		return id;
	}
	public void setId(int id) {
		this.id = id;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}

	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + id;
		return result;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj) {
			return true;
		}
		if (obj == null) {
			return false;
		}
		if (!(obj instanceof User)) {
			return false;
		}
		User other = (User) obj;
		if (id != other.id) {
			return false;
		}
		return true;
	}
    
}

 

分享到:
评论

相关推荐

    Java容器集合(equals 和 hashCode+基础数据结构+ArrayList+Vector和LinkedList)

    重写equals方法需要遵守通用约定,hashCode方法的实现需要保证equals相同的两个对象的hashCode结果一致。ArrayList、Vector和LinkedList是Java中三种常见的容器集合,每种集合都有其特点和应用场景。

    学习Object类——为什么要重写equeals和hashcode方法

    此外,Object 类的设计是为了扩展,它提供了一些非 final 方法,如 equals、hashCode、toString、clone 和 finalize,这些方法都有通用的约定,需要在子类中被覆盖(override)。如果不遵守这些约定,依赖这些约定的...

    Java中Equals使用方法汇总

    重写`equals`方法时,应遵循以下通用约定: 1. **自反性**:对于任何对象`x`,`x.equals(x)`应返回`true`。 2. **对称性**:如果`y.equals(x)`返回`true`,那么`x.equals(y)`也应该返回`true`。 3. **传递性**:...

    详解hashCode()和equals()的本质区别和联系

    在重写 equals() 方法时,需要遵守通用约定,包括: 1. 自反性:对于任意的引用值 x,x.equals(x) 一定为 true。 2. 对称性:对于任意的引用值 x 和 y,当 x.equals(y) 返回 true 时,y.equals(x) 也一定返回 true...

    java中equals和==的区别.pdf

    在Java编程语言中,`equals()`方法和`==`运算符是两个经常用来比较对象的...同时,为了确保其他自定义类的`equals()`方法正确实现,应遵循通用约定,即同时重写`equals()`和`hashCode()`方法,以保持一致性和正确性。

    高效Java 第三部分,经典的国外教材

    这些方法在设计时考虑到了它们可能会被子类覆盖,因此有着明确的通用约定。正确地理解和使用这些方法对于编写高质量的Java代码至关重要。本文将重点介绍 `equals()` 方法的正确实现方式及其通用约定。 #### 二、`...

    NET设计规范-.NET约定、惯用法与模式.part2

    8.7.1 Object.Equals 210 8.7.2 Object.GetHashCode 212 8.7.3 Object.ToString 213 8.8 Uri 214 8.9 System.Xml的使用 216 8.10 相等性操作符 218 8.10.1 值类型的相等性操作符 218 8.10.2 引用类型...

    NET设计规范-.NET约定、惯用法与模式.part1

    8.7.1 Object.Equals 210 8.7.2 Object.GetHashCode 212 8.7.3 Object.ToString 213 8.8 Uri 214 8.9 System.Xml的使用 216 8.10 相等性操作符 218 8.10.1 值类型的相等性操作符 218 8.10.2 引用类型...

    JPL_Coding_Standard_Java

    - 重写`equals()`方法时,应接受一个`Object`类型的参数。 - 这样的设计可以确保方法的通用性。 #### 内存管理 13. **R13:不使用终结器(finalizer)** - 终结器在某些情况下可能导致不确定的行为和性能问题。 ...

    core java达内培训资料

    根据给定的文件信息,以下是对“core java...Object是所有Java类的根类,提供了如toString()、equals()、hashCode()等基础方法,用于支持对象的基本操作。理解并适当重写这些方法对于编写高质量的Java代码至关重要。

    java 编程规则

    - 创建通用类时,应包含常用的方法:`equals()`、`hashCode()`、`toString()`、`clone()`(实现`Cloneable`)和`Serializable`接口。 3. **测试代码**: - 为每个类提供一个`main()`方法用于测试,方便检查和验证...

    面向对象OO程序设计概念PPT课件.PPTx

    **Java中的Object类**:在Java中,所有类都直接或间接地继承自Object类,它提供了所有Java对象通用的行为,如toString()、equals()和hashCode()等方法。通过继承,我们可以为特定类添加额外的功能,同时保持与Java...

    java语言要点总结

    `Object`类是所有类的超类,提供了一些基本的方法如`toString()`、`equals()`等。`Class`类代表了类的类型信息,用于反射操作。 #### 十三、反射与内部类 反射允许运行时查询和修改类的行为,增强了程序的灵活性。...

    平台Java代码编程规范

    - 重写`equals()`和`hashCode()`方法时,确保满足对象相等性的约定。 - 尽可能使用接口而非具体类进行类型声明,提高代码的灵活性。 【开发EJB规则】 - 遵守Enterprise JavaBeans(EJB)的官方规范,如实体Bean、...

    2017Android面试试题

    "=="比较的是两个对象的内存地址,而equals()方法用于比较两个对象的内容是否相等。如果两个对象是同一个对象,那么它们的"=="和equals()结果相同。如果equals()未被重写,它默认的行为与"=="相同。 【Java反射机制...

    C#编码规约

    - **Equals()与GetHashCode()**:实现这两个方法时,要确保一致性,以支持哈希表等集合的操作。 - **Clone()**:实现克隆功能时,注意深拷贝与浅拷贝的区别。 - **默认构造器**:每个类都应至少有一个构造器,即使它...

    Java极品学习资料

    **String类为什么复写Object类的equals方法** - `equals`方法用于比较字符串的内容是否相等。 - 示例代码展示如何使用`equals`方法比较字符串。 #### 七、IO **字节流复制文件** - 使用字节流读取文件并将其写入新...

    Java编程规范

    8. **equals()与hashCode()**:当重写`equals()`方法时,务必也重写`hashCode()`,以保持对象的等价性。同时,这两个方法的行为应符合Object类中的定义。 9. **final**:在不打算修改的变量前使用`final`关键字,...

    我的java笔记

    - **Object类**:所有Java类的基类,提供了toString()、equals()等通用方法。 - **内部类**:定义在另一个类内部的类,分为成员内部类、局部内部类、静态内部类和匿名内部类。 - **集合**:Java集合框架,包括List、...

Global site tag (gtag.js) - Google Analytics