Lombok 使用教程-@EqualsAndHashCode | 自动生成类的 Equals 和 HashCode 方法

作者简介:大家好,我是smart哥,前中兴通讯、美团架构师,现某互联网公司CTO

联系qq:184480602,加我进群,大家一起学习,一起进步,一起对抗互联网寒冬

学习必须往深处挖,挖的越深,基础越扎实!

阶段1、深入多线程

阶段2、深入多线程设计模式

阶段3、深入juc源码解析


阶段4、深入jdk其余源码解析


阶段5、深入jvm源码解析

码哥源码部分

码哥讲源码-原理源码篇【2024年最新大厂关于线程池使用的场景题】

码哥讲源码【炸雷啦!炸雷啦!黄光头他终于跑路啦!】

码哥讲源码-【jvm课程前置知识及c/c++调试环境搭建】

​​​​​​码哥讲源码-原理源码篇【揭秘join方法的唤醒本质上决定于jvm的底层析构函数】

码哥源码-原理源码篇【Doug Lea为什么要将成员变量赋值给局部变量后再操作?】

码哥讲源码【你水不是你的错,但是你胡说八道就是你不对了!】

码哥讲源码【谁再说Spring不支持多线程事务,你给我抽他!】

终结B站没人能讲清楚红黑树的历史,不服等你来踢馆!

打脸系列【020-3小时讲解MESI协议和volatile之间的关系,那些将x86下的验证结果当作最终结果的水货们请闭嘴】

一、简介

任何类的定义都可以用@EqualsAndHashCode来注释,让lombok生成equals(Object other)hashCode()方法的实现。默认情况下,它将使用所有非静态、非瞬时的字段,但是你可以通过用@EqualsAndHashCode.Include@EqualsAndHashCode.Exclude标记类型成员来修改哪些字段被使用(甚至指定各种方法的输出被使用)。另外,你可以通过用@EqualsAndHashCode.Include标记它们并使用@EqualsAndHashCode(onlyExplicitlyIncluded = true)来精确指定你希望使用的字段或方法。

如果将@EqualsAndHashCode应用于一个子类,这个功能就变得有点棘手了。通常情况下,为这类自动生成equalshashCode方法是个坏主意,因为父类也定义了字段,这些字段也需要equals/hashCode代码,但这些代码不会被生成。通过设置callSupertrue,你可以在生成的方法中包含你的父类的equalshashCode方法。对于hashCodesuper.hashCode()的结果被包含在哈希算法中,而在equals,如果父类实现认为它不等于传入的对象,生成的方法将返回false。请注意,并非所有的equals实现都能正确处理这种情况。然而,lombok生成的equals实现会正确处理这种情况,所以如果你的父类也有一个lombok生成的equals方法,你可以安全地调用它。如果你有一个明确的父类,你将被迫为callSuper提供一些值,以确认你已经考虑了它;如果不这样做,将导致一个警告。

当你不扩展任何东西(其实扩展了java.lang.Object)时,将callSuper设置为true是一个编译时错误,因为它将使生成的equals()hashCode()实现具有与简单地从java.lang.Object继承这些方法一样的行为:只有相同的对象才会互相相等,并且会有相同的hashCode。当你扩展另一个类时,不把callSuper设置为true会产生一个警告,因为除非父类没有(重要的)字段,否则lombok不能为你生成一个考虑到你的父类所声明的字段的实现。你需要编写你自己的实现,或者依靠callSuper的链式工具。你也可以使用lombok.equalsAndHashCode.callSuper配置键。

Lombok 0.10中的新内容:除非你的类是final并且扩展了java.lang.Object,否则lombok会生成一个canEqual方法,这意味着JPA代理仍然可以和他们的基类相等,但是添加新状态的子类不会破坏等价合约。本文将解释为什么需要这样一个方法的复杂原因:如何在Java中编写一个等价方法。如果一个层次结构中的所有类都是scala案例类和带有lombok生成的等价方法的类的混合体,那么所有的等价都会 “正常工作”。如果你需要写你自己的equals方法,如果你改变了equalshashCode,你应该总是覆盖canEqual

Lombok 1.14.0中的新增功能:要在equals(如果相关,还有canEqual)方法的另一个参数上添加注释,可以使用onParam=@_u({@AnnotationsHere})。不过要小心!这是一个实验特性。有关更多详细信息,请参阅有关onX功能的文档。

Lombok 1.18.16中的新内容:生成的hashCode()的结果可以通过设置cacheStrategyCacheStrategy.NEVER以外的值进行缓存。如果注释类的对象可以以任何方式被修改,从而导致hashCode()的结果改变,请不要使用这个方法。

二、示例比较

1. Lombok 写法

    import lombok.EqualsAndHashCode;
    
    @EqualsAndHashCode
    public class EqualsAndHashCodeExample {
      private transient int transientVar = 10;
      private String name;
      private double score;
      @EqualsAndHashCode.Exclude private Shape shape = new Square(5, 10);
      private String[] tags;
      @EqualsAndHashCode.Exclude private int id;
      
      public String getName() {
        return this.name;
      }
      
      @EqualsAndHashCode(callSuper=true)
      public static class Square extends Shape {
        private final int width, height;
        
        public Square(int width, int height) {
          this.width = width;
          this.height = height;
        }
      }
    }

2. Java 标准写法

    import java.util.Arrays;
    
    public class EqualsAndHashCodeExample {
      private transient int transientVar = 10;
      private String name;
      private double score;
      private Shape shape = new Square(5, 10);
      private String[] tags;
      private int id;
      
      public String getName() {
        return this.name;
      }
      
      @Override public boolean equals(Object o) {
        if (o == this) return true;
        if (!(o instanceof EqualsAndHashCodeExample)) return false;
        EqualsAndHashCodeExample other = (EqualsAndHashCodeExample) o;
        if (!other.canEqual((Object)this)) return false;
        if (this.getName() == null ? other.getName() != null : !this.getName().equals(other.getName())) return false;
        if (Double.compare(this.score, other.score) != 0) return false;
        if (!Arrays.deepEquals(this.tags, other.tags)) return false;
        return true;
      }
      
      @Override public int hashCode() {
        final int PRIME = 59;
        int result = 1;
        final long temp1 = Double.doubleToLongBits(this.score);
        result = (result*PRIME) + (this.name == null ? 43 : this.name.hashCode());
        result = (result*PRIME) + (int)(temp1 ^ (temp1 >>> 32));
        result = (result*PRIME) + Arrays.deepHashCode(this.tags);
        return result;
      }
      
      protected boolean canEqual(Object other) {
        return other instanceof EqualsAndHashCodeExample;
      }
      
      public static class Square extends Shape {
        private final int width, height;
        
        public Square(int width, int height) {
          this.width = width;
          this.height = height;
        }
        
        @Override public boolean equals(Object o) {
          if (o == this) return true;
          if (!(o instanceof Square)) return false;
          Square other = (Square) o;
          if (!other.canEqual((Object)this)) return false;
          if (!super.equals(o)) return false;
          if (this.width != other.width) return false;
          if (this.height != other.height) return false;
          return true;
        }
        
        @Override public int hashCode() {
          final int PRIME = 59;
          int result = 1;
          result = (result*PRIME) + super.hashCode();
          result = (result*PRIME) + this.width;
          result = (result*PRIME) + this.height;
          return result;
        }
        
        protected boolean canEqual(Object other) {
          return other instanceof Square;
        }
      }
    }

三、支持的配置项

lombok.equalsAndHashCode.doNotUseGetters = [true | false] (默认: false)
如果设置为truelombok在生成equalshashCode方法时将直接访问字段而不是使用getters(如果有的话)。注释参数 “doNotUseGetters”,如果明确指定的话,将优先于这个设置。

lombok.equalsAndHashCode.callSuper = [call | skip | warn] (默认: warn)
如果设置为calllombok将为子类生成对hashCodeequals的父类实现的调用。如果设置为skip,则不会产生这样的调用。默认行为warnskip一样,只是多了一个额外的警告。

lombok.equalsAndHashCode.flagUsage = [warning | error] (默认: not set)
Lombok@EqualsAndHashCode的任何使用标记为警告或错误(如果已配置)。

四、附属说明

Arrays是 "深度 "比较/哈希编码,这意味着包含自己的数组将导致StackOverflow错误。不过,这种行为与ArrayList等没有区别。

您可以放心地假设所使用的hashCode实现不会在lombok的不同版本之间发生变化,但是这种保证并不是一成不变的;如果使用另一种哈希算法可以显著提高性能,那么将在未来的版本中进行替换。

出于相等的目的,浮点数和双精度浮点数的2NaN(不是数字)值被视为相等,虽然'NaN==NaN’返回false。这类似于java.lang.Doubleequals方法,并且实际上是确保将对象与自身的精确副本进行比较时返回true表示相等所必需的。

如果有任何名为hashCodeequals的方法,无论其返回类型如何,都不会被生成,而是发出一个警告。这两个方法需要彼此同步,除非lombok生成所有的方法,否则无法保证,因此如果一个或两个方法已经存在,你总是会得到一个警告。你可以用@lombok.experimental.Tolerate标记任何方法,以便从lombok中隐藏它们。

尝试排除不存在或无论如何都会被排除的字段(由于它们是静态或瞬态的)会在命名字段上产生警告。

如果一个方法被标记为包含,并且它与一个字段有相同的名称,那么它将取代该字段(方法被包含,字段被排除)。

lombok 1.16.22之前,包含/排除可以通过@EqualsAndHashCode注解的ofexclude参数完成。这种旧式的包含机制仍然被支持,但在未来将被废弃。

默认情况下,任何以$符号开头的变量都被自动排除。你只能通过用@EqualsAndHashCode.Include标记它们来包括它们。

如果一个要包含的字段存在一个getter,它将被调用,而不是使用一个直接的字段引用。这种行为可以使用字段引用:

    @EqualsAndHashCode(doNotUseGetters = true)

如果您已经通过lombok.configlombok.addNullAnnotations配置了nullity注释风格,则生成的equals方法以及任何canEqual方法的参数都将使用可为Null的注释进行注释。如果将@NonNullByDefault样式注释与严格的空性检查结合使用,则需要执行此操作。

参考文献

【1】@EqualsAndHashCode | Equality made easy: Generates hashCode and equals implementations from the fields of your object.

`@EqualsAndHashCode` 是一个 Lombok 注解,它可以自动生成 `equals()` 和 `hashCode()` 方法。这两个方法在进行对象比较和使用集合(如 HashSet、HashMap)时非常有用。 `equals()` 方法用于比较两个对象的内容是否相等,通常需要重写该方法来自定义对象的相等性判断。默认情况下,`equals()` 方法比较的是对象的引用是否相等,即判断两个对象是否指向同一块内存地址。 `hashCode()` 方法用于计算对象的哈希码值。哈希码值在使用哈希表数据结构(如 HashSet、HashMap)时非常有用,它帮助快速定位对象在哈希表中的位置。默认情况下,`hashCode()` 方法根据对象的内存地址计算哈希码值。 如果使用默认的 `equals()` 和 `hashCode()` 方法进行两个对象的比较,将会得到以下结果: 1. `equals()` 方法:默认实现比较对象的引用是否相等,即判断两个对象是否指向同一块内存地址。如果两个对象的引用不同,则返回 `false`;如果两个对象的引用相同,则返回 `true`。 2. `hashCode()` 方法:默认实现使用对象的内存地址计算哈希码值。由于每个对象的内存地址是唯一的,因此每个对象的哈希码值也会是唯一的。因此,使用默认的 `hashCode()` 方法来比较两个对象的哈希码值将始终返回不相等的结果。 为了更准确地比较对象的内容,通常需要重写 `equals()` 方法,并确保在 `equals()` 方法中比较对象的字段值而不是引用。同时,应该重写 `hashCode()` 方法以保证根据对象内容计算哈希码值。 Lombok 的 `@EqualsAndHashCode` 注解可以自动生成重写 `equals()` 和 `hashCode()` 方法的代码,它会基于中的字段来生成相应的逻辑,以实现对象内容的比较和哈希码值的计算。这样可以避免手动编写这些重复且繁琐的代码。 总结而言,如果使用默认的 `equals()` 和 `hashCode()` 方法进行两个对象的比较,将会比较对象的引用和内存地址,而不是对象的内容。要进行准确的对象比较和哈希码计算,通常需要重写这两个方法,并根据对象的字段来判断相等性和计算哈希码值。使用 Lombok 的 `@EqualsAndHashCode` 注解可以自动生成这些方法的代码。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值