提示十

作者: 飞絮搅青冥 | 来源:发表于2022-05-16 22:15 被阅读0次

    今天来看提示十:覆盖equals时需要遵守的通用约定。

    当equals没有被覆盖时,比较的是内存中的地址,类的每个实例都只和自身相等,当满足下面条件时,我们不会去覆盖equals方法:

    • 每个类的实例都是固有唯一的。
    • 不太关心类是否提供了逻辑相等的测试功能。
    • 父类已经重写了 equals 方法,则父类行为完全适合于该子类。
    • 类是私有的或包级私有的,可以确定它的 equals 方法永远不会被调用。

    接下来就是重写equals时需要注意的五个原则:

    • 自反性: 对于任何非空引用 x, x.equals(x) 必须返回 true。
    • 对称性: 对于任何非空引用 x 和 y,如果且仅当 y.equals(x) 返回 true 时 x.equals(y) 必须返回 true。
    • 传递性: 对于任何非空引用 x、y、z,如果 x.equals(y) 返回 true,y.equals(z) 返回 true,则 x.equals(z) 必须返回 true。
    • 一致性: 对于任何非空引用 x 和 y,如果在 equals 比较中使用的信息没有修改,则x.equals(y) 的多次调用必须始终返回 true 或始终返回 false。
    • 非空性:对于任何非空引用 x, x.equals(null) 必须返回 false。

    这五点看起来非常直接,我一开始也感觉这些都是理所应当。但是作者介绍了继承和equals的情况后我就有了不一样的想法,难怪父类重写了equals方法后,子类就不推荐重写了。

    比如A类有一个属性a,重写了equals只要a相等,那么对象相同。B类继承A,并且多了一个b属性,想当然,它的equals方法就需要比较a,b两个属性。这样做的话,我们就会发现A.equals(B)没有问题,但是B.equals(A)就是false了。如果为了解决这对称性的问题,我们在B的equals方法中进行分类,如果对象是A,那么就只比较a属性,如果对象是B,那么就比较a,b属性,否则就返回false。这样做看似解决了对称性问题,但是当我们引入B1,B2两个不同的B对象,那么就容易得到B1.equals(A),A.equals(B2),但是B1和B2又显然不相同,这样又违反了传递性。所以当我们遇到这种情况时,作者推荐组合而非继承。通过组合的方式可以有效解决这个问题。

    试图解决对称性会引入传递性问题

    以下是编写高质量 equals 方法的配方:

    1. 使用==操作符检查参数是否为这个对象的引用, 如果是, 则返回true。
    2. 使用instanceof操作符检查参数是否为正确的类型, 如果不是, 则返回false. 注意这个地方不能使用getClass替换instanceof,这样不符合里氏替换原则。
    3. 把参数转换成正确的类型。
    4. 对于该类中的每个关键域, 检查参数中的域是否与该对象中对应的域相匹配。

    现在idea等工具都提供了一键生成equals等方法,我以前也用过,但是需要注意equals的格式是可以自己选择的,有时候会生成带有getClass的方法,可能我们需要注意一下。

    最后是三条提醒:

    1. 当重写 equals 方法时,同时也要重写 hashCode 方法
    2. 不要让 equals 方法试图太聪明。
    3. equal 时方法声明中,不要将参数 Object 替换成其他类型。因为这样是重载,并不是覆盖重写。应该养成良好的习惯,在覆盖方法的时候加上@Override注解,及时发现这些错误。

    总之,除非必须:在很多情况下,不要重写 equals 方法,从 Object 继承的实现完全是你想要的。如果你确实重写了 equals 方法,那么一定要比较这个类的所有重要属性,并且以保护前面 equals 约定里五个规定的方式去比较。

    相关文章

      网友评论

          本文标题:提示十

          本文链接:https://www.haomeiwen.com/subject/jdnwurtx.html