8.2.3 覆写 Equals

  经过对四种不同类型判等方法的讨论,我们不难发现不管是 Equals 静态方法、Equals 虚方法 抑或==操作符的执行结果,都可能受到覆写 Equals 方法的影响。因此研究对象判等就必须将注意 力集中在自定义类型中如何实现 Equals 方法,以及实现怎样的 Equals 方法。因为,不同的类型, 对于“相等”的理解会有所偏差,你甚至可以在自定义类型中实现一个总是相等的类型,例如:

class AlwaysEquals
{
public override bool Equals(object obj)
{
return true;
}
}

  因此,Euqls 方法的执行结果取决于自定义类型的具体实现规则,而.NET 又为什么提供这种机 制来实现对象判等策略呢?首先,对象判等决定于需求,没有必要为所有.NET 类型完成逻辑判等, System.Object 基类也无法提供满足各种需求的判等方法;其次,对象判等包括值判等和引用判等 两个方面,不同的类型对判等的处理又有所不同,通过多态机制在派生类中处理各自的判等实现 显然是更加明智与可取的选择。

  接下来,我们开始研究如何通过覆写 Equals 方法实现对象的判等。覆写 Equals 往往并非易事, 要综合考虑到对值类型字段和引用类型字段的分别判等处理,同时还要兼顾父类覆写所带来的影 响。不适当的覆写会引发意想不到的问题,所以必须遵循三个等价原则:自反、传递和对称,这 是实现 Equals 的通用契约。那么又如何为自定义类型实现 Equals 方法呢?

  最好的参考资源当然来自于.NET 框架类库的实现,事实上,关于 Equals 的覆写在.NET 中已经 有很多的基本类型完成了这一实现。从值类型和引用类型两个角度来看:

  对于值类型,基类 System.ValueType 通过反射机制覆写了 Equals 方法来比较两个对象的值相 等,但是这种方式并不高效,更明智的办法是在自定义值类型时有针对性的覆写 Equals 方法, 来提供更灵活、高效的处理机制。

  对于引用类型,覆写 Equals 方法意味着要改变 System.Object 类型提供的引用相等语义。那么, 覆写 Equals 要根据类型本身的特点来实现,在.NET 框架类库中就有很多典型的引用类型实现 了值相等语义。例如 System.String 类型的两个变量相等意味着其包含了相等的内容,System. Version 类型的两个变量相等也意味着其 Version 信息的各个指标分别相等。

  因此对 Equals 方法的覆写主要包括对值类型的覆写和对引用类型的覆写,同时也要区别基类 是否已经有过覆写和不曾覆写两种情况,并以等价原则为前提,进行判断。在此,我们仅提供较 为标准的实现方法,具体的实现取决于不同的类型定义和语义需求。

    class EqualsEx
    {
        //定义值类型成员 ms
        private MyStruct ms;
        //定义引用类型成员 mc
        private MyClass mc;
        public override bool Equals(object obj)
        {
            //为 null,则必不相等
            if (obj == null) return false;
            //引用判等为真,则二者必定相等
            if (ReferenceEquals(this, obj)) return true;
            //类型判断
            EqualsEx objEx = obj as EqualsEx;
            if (objEx == null) return false;
            //最后是成员判断,分值类型成员和引用类型成员
            //通常可以提供强类型的判等方法来单独处理对各个成员的判等
            return EqualsHelper(this, objEx);
        }
        private static bool EqualsHelper(EqualsEx objA, EqualsEx objB)
        {
            //值类型成员判断
            if (!objA.ms.Equals(objA.ms)) return false;
            //引用类型成员判断
            if (!Equals(objA.mc, objB.mc)) return false;
            //最后,才可以判定两个对象是相等的
            return true;
        }
    }

    internal struct MyStruct
    {
    }

    internal class MyClass
    {
    }

  上述示例只是从标准化的角度来阐释 Equals 覆写的简单实现,而实际应用时又会有所不同, 然而总结起来实现 Equals 方法我们应该着力于以下几点:首先,检测 obj 是否为 null,如果是则 必然不相等;然后,以 ReferenceEquals 来判等是否引用相等,这种办法比较高效,因为引用相等 即可以推出值相等;然后,再进行类型判断,不同类型的对象一定不相等;最后,也是最复杂的 一个过程,即对对象的各个成员进行比较,引用类型进行恒定性判断,值类型进行恒等性判断。 在本例中我们将成员判断封装为一个专门的处理方法 EqualsHelper,以隔离对类成员的判断实现, 主要有以下几个好处:

  符合 Extract Method 原则,以隔离相对变化的操作。

  提供了强类型版本的 Equals 实现,对于值类型成员来说还可以避免不必要的装箱操作。

  为==操作符提供了重载实现的安全版本。

在.NET 框架中,System.String 类型的 Equals 覆写方法就提供了 EqualsHelper 方法来实现。

原文地址:https://www.cnblogs.com/kikyoqiang/p/10347828.html

时间: 2024-11-03 03:47:30

8.2.3 覆写 Equals的相关文章

为什么覆写equals必须要覆写hashCode?

============================================= 原文链接: 为什么覆写equals必须要覆写hashCode? 转载请注明出处! ============================================= <Effective java>Chapter Three  Item 9:Always override hashCode when you overried equals. (下载链接: Effective_java(英文版)第

java覆写equals方法

何时需要重写equals() 当一个类有自己特有的“逻辑相等”概念(不同于对象身份的概念). object规范规定,如果要重写equals(),也要重写hashcode() 如何覆写equals() 覆写equals方法 1  使用instanceof操作符检查“实参是否为正确的类型”. 2  对于类中的每一个“关键域”,检查实参中的域与当前对象中对应的域值. 3. 对于非float和double类型的原语类型域,使用==比较: 4  对于对象引用域,递归调用equals方法: 5  对于flo

覆写equals方法为什么需要覆写hashCode方法

覆写equals方法必须覆写hashCode方法,是JDK API上反复说明的,不过为什么要这样做呢?这两个方法之间有什么关系呢? void test() { // Person类的实例作为Map的key Map<Person, Object> map = new HashMap<Person, Object>(); map.put(new Person("张三"), new Object()); // Person类的实例作为List的元素 List<P

java 覆写Object.equals() 方法

1 //覆写Object.equals 2 class Demo { 3 private String name; 4 private int age; 5 public Demo(String name,int age) { 6 this.name=name; 7 this.age=age; 8 } 9 public String toString() { 10 return this.name+this.age; 11 } 12 public boolean equals(Object ob

11.JAVA-Object类之finalize(),clone(),toString()等方法覆写

1.Object介绍 Object类是一个特殊的类,是所有类(包括数组,接口 )的父类,如果一个类没有用extends明确指出继承于某个类,那么它默认继承Object类,所以可以通过向上转型的方法使用类型为Object的变量指向任意类型的对象. 本章主要实现覆写Object类的clone(),toString()equals(),finalize()等最重要的方法. 2.覆写finalize() 在之前我们学习析构方法时,便讲过finalize(). 参考之前学的finallize方法示例:3.

object 类 toString() 和 equals() 的覆写

基本作用: objiect类是所有类的父类. 任何一个类定义的时候如果没有明确定义了父类的话,默认父类是Object类. class A extends Object{} 在整个java里面,类的继承关系一直存在的.例如: String 类 这时候一个好处就出现了,利用Object类可以接受一切类. Object obj = new Book() ://向上转型 Obect obj = “Hello”  : //向上转型 如果不知道参数类型的时候,使用Object是最好的选择 http://do

使用第三方工具覆写Object中方法

我们在实际开发中,经常需要覆写Object中的equals,hashcode,toString方法,其实编写这些代码并不是很难,但很枯燥和乏味. 下面推荐Google的Guava jar包来覆写上面的方法. 直接上代码. import com.google.common.base.MoreObjects; import com.google.common.base.Objects; import com.google.common.collect.ComparisonChain; public

Java重载、覆写、this、super、抽象类、接口

第一.重载和覆写 1) 重载:方法名相同,参数类型和个数不同,典型的是构造方法的重载  覆写:方法名.参数类型.返回值类型全部相同,典型的是接口的实现: 2)重载对权限没有要求:覆写要求  被覆写的方法不能拥有更严格的权限: 3)重载发生再一个类中:覆写发生在继承类中. 第二.this和super 1)this访问本类中的属性(方法),如果本类中没有此属性(方法),则从父类中 继续寻找:super询问父类中的属性(方法): 2)调用本类构造,必须放在构造方法的首行:调用父类构造,必须放在子类构造

Objective-C-类(static)方法、实例方法、overwrite(覆写)、属性(property)复习

先来定义一个Human父类 定义部分: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 // //  Human.h //  OOP // //  Created by jimmy.yang on 11-2-9. //  Copyright 2011 __MyCompanyName__. All rights reserved. // #import <Foundation/Foundation.h> @interface Human :