zoukankan      html  css  js  c++  java
  • 重写equals方法(未完)

      equals方法是我们日常编程中很常见的方法,Object中对这个方法的解释如下:

     boolean

    equals(Object obj) 指示其他某个对象是否与此对象相等

      查看该方法的底层代码如下:

    public boolean equals(Object obj) {
            return (this == obj);
        }

    通过上面的代码很容易就能看出来,Objectequals方法上是用来比较两个实例是否为同一个实例对象,底层还是依赖于==的判断。

    2.4.1.1 什么情况下无需覆盖equals方法

    Equals方法看起来是个很简单的方法,但是重写equals方法还是很容易出现问题的,并不是说每个类都需要来重写equals方法。满足下面任意条件的类都可以不用覆盖equals方法;

    • 类的每个实例本质上都是唯一的,如枚举,单例等;
    • 不关心类是否提供了逻辑相等的测试功能。Random覆盖equals,检查两个Random实例是否产生相同的随机数序列,但是这其实是很不必要的,所以从Object中继承来的equals方法已经足够了。
    • 超类已经覆盖了equals方法,从超类集成过来的行为对于子类也是合适的。List实现从ArrayList继承equals实现。
    • 类是私有的或者包访问权限的,可以确定它的equals方法永远不会被调用。

    通过上面的叙述,知道了只要满足四种情况,那么无疑覆盖equals方法,既是多余也容易出现问题,我们应该加以避免。

    2.4.1.2 覆盖equals方法

      了解了无需覆盖equals的条件,那么什么条件中覆盖equals方法呢?覆盖equals需要注意什么呢?

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

      上面通常属于“值类(value class”的情形。值类仅仅是一个表示值的类,如IntegerDateString等。程序员在利用equals方法来比较值对象的引用时,希望知道逻辑上的值是否相等,而并非比较是否指向同一个对象。为了满足程序员的要求,覆盖equals是很必须的,这样做也使得这个类的实例可以用做映射表(map)的键(key),或者集合(set)的元素,使映射表或map中表现出预期的行为,如不存放具有相同值。

      但是Java中有一种值类却不在这一要求之中,那就是枚举。在覆盖equals方法时,必须遵循以下的通用约定:

    • 自反性(reflexive):对于任何非null的引用值xx.equals(x)必须返回true
    • 对称性(symmetric):对于任何非null的引用值xy,当且仅当y.equals(x)返回true时,x.equals(y)必须返回true
    • 传递性(transitive):对于任何非null的引用值xyz,如果x.equals(y)返回true,且y.equals(z)返回true,那么x.equals(z)也必须返回true
    • 一致性(consistent):对于任何非null的引用值xy,只有equals的比较操作在对象中所用的信息没有被修改,多次调用x.equals(y)总会一致地返回相同的结果。
    • 非空性(Non-nullity):对于任何非null的引用值xx.equals(null)必须返回false

      这些约定看起来很简单,但是在实际编程中很容易违反这些约定,一旦违反这些约定,程序运行就不正常,接下来通过例子展示违反约定的情况以及如何避免这些错误。

    自反性

      自反性:如果我们书写的equals方法形式的功能不是用于判断自己的对象是否等于自身(或是自身包含的值),那么该会是多么可怕的事情呢?如List中存放实例,然后List判断contains()该对象时,那么就会永远都会返回false;通过代码来验证一下:

    package cn.object.override.equals;
    import java.util.ArrayList;
    import java.util.List;
    import org.junit.Test;
    public class EqualsDemo {
        
        @Test
        public void test(){
            EqualsDemo demo1 = new EqualsDemo();
            EqualsDemo demo2 = new EqualsDemo();
            List<EqualsDemo> list = new ArrayList<>();
            list.add(demo1);
            System.out.println("list是否包含demo1:"+list.contains(demo1));
            System.out.println("list是否包含demo2:"+list.contains(demo2));
        }
        @Override
        public boolean equals(Object obj) {
            // TODO Auto-generated method stub
            return false;
        }
    }

      我们通过ArrayList的底层代码知道contains是通过调用当前类的indexOf方法来确定的,而indexOf是通过循环遍历判断参数和集合中的元素是否相等,而这个判断是通过实例的equals方法来确定的,所以上面的两个判断都是false也就可以理解了,这就是equals不正确覆盖带来的严重后果了;

    对称性

      对称性也是很重要的一个要求,并且逻辑思维也应该是这样的,那么如果我们不正常的覆盖,导致情况不是这样的会怎么样呢?通过一个代码来实现;

    package cn.object.override.equals;
    import org.junit.Test;
    /**
     * @author YorickYou
     * Code Address:https://github.com/YorickYou/JavaSE.git
     */
    public class CaseInsensitiveString {
        private final String s;
        //有参构造
        public CaseInsensitiveString(String s) {
            if (s == null)
                throw new NullPointerException();
            this.s = s;
        }
        @Override
        public boolean equals(Object obj) {
            if(obj instanceof CaseInsensitiveString)
                return s.equalsIgnoreCase(((CaseInsensitiveString) obj).s);
            if (obj instanceof String) 
                return s.equalsIgnoreCase((String)obj);
            return false;
        }
        public static void main(String[] args) {
            CaseInsensitiveString string1 = new CaseInsensitiveString("abc");
            String str = "abc";
            //两次调用equals方法,前者是调用的CaseInsensitiveString的equals方法,后者是String的equals方法
            System.out.println(string1.equals(str)+"自反性"+str.equals(string1));
        }
    }

      上面对equals方法的覆盖就严重违反了对称性.如果我们往集合中添加该对象,那么此时会出现什么情况呢?

    public static void test1(){
            List<CaseInsensitiveString> list = new ArrayList<CaseInsensitiveString>();
            CaseInsensitiveString string1 = new CaseInsensitiveString("abc");
            String str = "abc";
            list.add(string1);
            System.out.println(string1.equals(str)+"--"+list.contains(str)+"--"+list.contains(string1));
        }//true--false--true

    所以在覆盖equals方法时,我们一定要注意对称性,对于上面问题的解决只需要将String的互操作这一段从equals中移除即可.

    package cn.object.override.equals;
    
    import java.util.ArrayList;
    import java.util.List;
    
    import org.junit.Test;
    
    /**
     * 
     * @author YorickYou
     * Code Address:https://github.com/YorickYou/JavaSE.git
     */
    public class CaseInsensitiveString {
        private final String s;
        //有参构造
        public CaseInsensitiveString(String s) {
            if (s == null)
                throw new NullPointerException();
            this.s = s;
        }    
        public static void main(String[] args) {
            CaseInsensitiveString string1 = new CaseInsensitiveString("abc");
            String str = "abc";
            //两次调用equals方法,前者是调用的CaseInsensitiveString的equals方法,后者是String的equals方法
            System.out.println(string1.equals(str)+"自反性"+str.equals(string1));
            test1();
        }
        
        @Override
        public boolean equals(Object obj) {
            // TODO Auto-generated method stub
            return obj instanceof CaseInsensitiveString && ((CaseInsensitiveString)obj).s.equalsIgnoreCase(s);
        }
        public static void test1(){
            List<CaseInsensitiveString> list = new ArrayList<CaseInsensitiveString>();
            CaseInsensitiveString string1 = new CaseInsensitiveString("abc");
            String str = "abc";
            list.add(string1);
        System.out.println(string1.equals(str)+"--"+list.contains(str)+"--"+list.contains(string1));
        }
    }

  • 相关阅读:
    机器学习实践笔记3(树和随机森林)
    Cocos2d-x3.1回调函数具体解释
    base 64 编解码器
    HDU 4915 Parenthese sequence _(:зゝ∠)_ 哈哈
    跟我extjs5(03--在项目过程中加载文件)
    备份和还原数据库
    Android学习–Android app 语言切换功能
    Android app内语言环境切换
    Android学习–Android app 语言切换功能
    swift:自定义UICollectionViewFlowLayout
  • 原文地址:https://www.cnblogs.com/lin-jing/p/8315171.html
Copyright © 2011-2022 走看看