@EqualsAndHashCode - 相等更简单:从对象的字段生成hashCode和equals实现

简介: @EqualsAndHashCode - 相等更简单:从对象的字段生成hashCode和equals实现

@EqualsAndHashCode

  1. 任意类的定义都可以添加@EqualsAndHashCode注解,让lombok帮你生成equals(Object other)hashCode()方法的实现。默认情况下会使用非静态和非transient型字段来生成,但是你也通过在字段上添加@EqualsAndHashCode.Include或者@EqualsAndHashCode.Exclude修改你使用的字段(甚至指定各种方法的输出)。或者你也可以通过在类上使用@EqualsAndHashCode(onlyExplicitlyIncluded = true),且在特定字段或特定方法上添加@EqualsAndHashCode.Include来指定他们。
  2. 如果将@EqualsAndHashCode添加到继承至另一个类的类上,这个功能会有点棘手。一般情况下,为这样的类自动生成equalshashCode方法是一个坏思路,因为超类也有定义了一些字段,他们也需要equals/hashCode方法但是不会自动生成。通过设置callSuper=true,可以在生成的equalshashCode方法里包含超类的方法。对于hashCode,·super.hashCode()·会被包含在hash算法内,而对于equals,如果超类实现认为它与传入的对象不一致则会返回false。注意:并非所有的equals都能正确的处理这样的情况。然而刚好lombok可以,若超类也使用lombok来生成equals方法,那么你可以安全的使用它的equals方法。如果你有一个明确的超类,             你得在callSuper上提供一些值来表示你已经斟酌过,要不然的话就会产生一条警告信息。
  3. 当你的类没有继承至任何类(非java.lang.Object, 当然任何类都是继承于Object类的),而你却将callSuer置为true, 这会产生编译错误(译者注: java: Generating equals/hashCode with a supercall to java.lang.Object is pointless. )。因为这会使得生成的equalshashCode方法实现只是简单的继承至Object类的方法,只有相同的对象并且相同的hashCode才会判定他们相等。若你的类继承至另一个类又没有设置callSuper, 则会产品一个告警,因为除非超类没有(或者没有跟相等相关的)字段,否则lombok无法为你生成考虑超类声明字段的实现。你需要编写自己的实现,或者依赖callSuper的链式功能。你也可以使用配置关键字lombok.equalsAndHashCode.callSuper.
  4. NEW in Lombok 0.10:
  5. 除非你的类是final类型并且只是继承至java.lang.Object, 否则lombok会生成一个canEqual方法,这以为着JPA代理仍然可以等于他们的基类,但是添加新状态的子类不会破坏equals契约。下文解释了为什么需要这种方法的复杂原因:How to Write an Equality Method in Java。如果层次结构中的所有类都是scala                 case类和带有lombok生成的equals方法的类的混合,则所有的相等都能正常工作。如果你需要编写自己的equals方法,那么如果更改equalshashCode方法,都应该始终覆盖canEqual.
  6. NEW in Lombok 1.14.0:
  7. 要将注释放在equals的另一个参数(以及相关的canEqual)方法上,可以使用onParam = @ __({@ AnnotationsHere})。 但要小心! 这是一个实验性功能。 有关更多详细信息,请参阅有关onX功能的文档。

看代码示例,最好自己写一下,然后查看编译后的class文件。

package com.amos.lombok;
import lombok.EqualsAndHashCode;
/**
 * @author amos
 */
@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;
        }
    }
    public static class Shape {
    }
}

编译后的class文件

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
package com.amos.lombok;
import java.util.Arrays;
public class EqualsAndHashCodeExample {
    private transient int transientVar = 10;
    private String name;
    private double score;
    private EqualsAndHashCodeExample.Shape shape = new EqualsAndHashCodeExample.Square(5, 10);
    private String[] tags;
    private int id;
    public EqualsAndHashCodeExample() {
    }
    public String getName() {
        return this.name;
    }
    public boolean equals(final Object o) {
        if (o == this) {
            return true;
        } else if (!(o instanceof EqualsAndHashCodeExample)) {
            return false;
        } else {
            EqualsAndHashCodeExample other = (EqualsAndHashCodeExample)o;
            if (!other.canEqual(this)) {
                return false;
            } else {
                label31: {
                    Object this$name = this.getName();
                    Object other$name = other.getName();
                    if (this$name == null) {
                        if (other$name == null) {
                            break label31;
                        }
                    } else if (this$name.equals(other$name)) {
                        break label31;
                    }
                    return false;
                }
                if (Double.compare(this.score, other.score) != 0) {
                    return false;
                } else {
                    return Arrays.deepEquals(this.tags, other.tags);
                }
            }
        }
    }
    protected boolean canEqual(final Object other) {
        return other instanceof EqualsAndHashCodeExample;
    }
    public int hashCode() {
        int PRIME = true;
        int result = 1;
        Object $name = this.getName();
        int result = result * 59 + ($name == null ? 43 : $name.hashCode());
        long $score = Double.doubleToLongBits(this.score);
        result = result * 59 + (int)($score >>> 32 ^ $score);
        result = result * 59 + Arrays.deepHashCode(this.tags);
        return result;
    }
    public static class Shape {
        public Shape() {
        }
    }
    public static class Square extends EqualsAndHashCodeExample.Shape {
        private final int width;
        private final int height;
        public Square(int width, int height) {
            this.width = width;
            this.height = height;
        }
        public boolean equals(final Object o) {
            if (o == this) {
                return true;
            } else if (!(o instanceof EqualsAndHashCodeExample.Square)) {
                return false;
            } else {
                EqualsAndHashCodeExample.Square other = (EqualsAndHashCodeExample.Square)o;
                if (!other.canEqual(this)) {
                    return false;
                } else if (!super.equals(o)) {
                    return false;
                } else if (this.width != other.width) {
                    return false;
                } else {
                    return this.height == other.height;
                }
            }
        }
        protected boolean canEqual(final Object other) {
            return other instanceof EqualsAndHashCodeExample.Square;
        }
        public int hashCode() {
            int PRIME = true;
            int result = super.hashCode();
            result = result * 59 + this.width;
            result = result * 59 + this.height;
            return result;
        }
    }
}

注意

超类不加callSuper且不手动覆写

上述的代码,通过下面的代码测试

public static void main(String[] args) {
        Square square1 = new Square(1, 2);
        Square square2 = new Square(1, 2);
        // false
        System.out.println(square1.equals(square2));
    }

可以看出,明明对象应该是相等的,但是就是不等!! 因为下面的代码导致,可以看出使用的是Object的equals方法,该方法一定要对象完全一样且hashCode一样才判定相等。

else if (!super.equals(o)) {
                    return false;
}

所以,为了避免这个问题,你要么手动覆写超类的equals,要么在超类上加callSuper注解。



相关文章
|
6月前
|
存储 算法 Java
为什么要重写 hashcode 和 equals 方法
为什么要重写 hashcode 和 equals 方法
54 0
|
3月前
|
存储 Java
|
4月前
|
Java 容器
equals与hashcode的区别与联系
equals与hashcode的区别与联系
|
6月前
|
存储 Java
为什么要重写hashCode()和equals()(深入了解)
为什么要重写hashCode()和equals()(深入了解)
|
存储 Java
引以为戒:避免在Set中使用未重写equals和hashCode的引用对象进行去重
在日常的Java开发中,我们经常会使用Set集合来实现去重操作,确保集合中不含有重复的元素。然而,如果使用未重写equals()和hashCode()方法的引用对象进行去重,可能会导致意外的行为,最近了在项目中就遇到了这个情况,让我们深入探讨这个问题,并引以为戒,确保正确实现去重操作。
62 0
引以为戒:避免在Set中使用未重写equals和hashCode的引用对象进行去重
|
算法 Java 索引
equals方法和hashCode方法之间的那些事(1.1)
equals方法和hashCode方法之间的那些事(1.1)
为什么要重写 hashcode 和 equals 方法?
为什么要重写 hashcode 和 equals 方法?
79 0
|
存储 缓存 Java
深入理解= = 、equals()与hashcode()的关系
理解= = 、equals()与hashcode()的关系
107 0
深入理解= = 、equals()与hashcode()的关系
|
存储 NoSQL
简单讲一下 HashCode() 与 equals()方法
简单讲一下 HashCode() 与 equals()方法.
109 0
hashCode和equals的区别
hashCode和equals的区别
323 0