Lombok 简介 以及 Lombok 的 @EqualsAndHashCode(callSuper = false) 使用说明

 

在认识 @EqualsAndHashCode 这个注解之前  , 我么首先需要知道 Lombok ,

 一 : Lombok 简介 

Lombok项目是一个Java库,它会自动插入编辑器和构建工具中,Lombok提供了一组有用的注释,用来消除Java类中的大量样板代码。仅五个字符(@Data)就可以替换数百行代码从而产生干净,简洁且易于维护的Java类。

Lombok 常用注解:

@Setter :注解在类或字段,注解在类时为所有字段生成setter方法,注解在字段上时只为该字段生成setter方法。

@Getter :使用方法同上,区别在于生成的是getter方法。

@ToString :注解在类,添加toString方法。

@EqualsAndHashCode: 注解在类,生成hashCode和equals方法。

@NoArgsConstructor: 注解在类,生成无参的构造方法。

@RequiredArgsConstructor: 注解在类,为类中需要特殊处理的字段生成构造方法,比如final和被@NonNull注解的字段。

@AllArgsConstructor: 注解在类,生成包含类中所有字段的构造方法。

@Data: 注解在类,生成setter/getter、equals、canEqual、hashCode、toString方法,如为final属性,则不会为该属性生成setter方法。

@Slf4j: 注解在类,生成log变量,严格意义来说是常量。

二 : Lombok 的 @Data 注解使用问题

       在上面我们阐述过了 Lombok 的@Date 的好处 , 以及用法 , @Data 帮助我们省略了@Setter,@Getter,@ToString等注解,一般对于普通的实体类使用该注解,不会出现什么问题,但是当我们把这个注解,使用在派生类上(子类),就会出现一个小问题。

 1 . 首先我们定义一个父类 Father01.java 

import lombok.Data;

/**
 * @description: 自定义父类
 * @author: huang
 * @create: 2020-07-22 19:42
 **/
@Data
public class Father01 {

    private int name;

    private String code;


}

2 . 再定义一个子类 , 去继承 Father01.java , 且让 Child01.java 拥有自己的属性  , 也使用@Data 注解该类

import lombok.Data;

/**
 * @description: 子类
 * @author: huang
 * @create: 2020-07-22 20:02
 **/
@Data
public class Child01 extends Father01 {
    
    private String address;
    
    private Long crd;

    public Father01(String name, String code) {
        this.name = name;
        this.code = code;
    }
    
}

3 . 此时 IDEA 或者 Eclipse 开发工具  , 会在子类的 @Data 注解上进行警告信息提示  , "Annotate class 'Child01' as @EqualsAndHashCode"

Lombok 简介 以及 Lombok 的 @EqualsAndHashCode(callSuper = false) 使用说明_第1张图片

4 . 按照提示 加上@EqualsAndHashCode 注解后  , 'Child01' 类就变成了这个样子 

import lombok.Data;
import lombok.EqualsAndHashCode;

/**
 * @description: 子类
 * @author: huang
 * @create: 2020-07-22 20:02
 **/
@EqualsAndHashCode(callSuper = true)
@Data
public class Child01 extends Father01 {

    private String address;

    private Long crd;


    public Child01(String address, long crd, String name, String code) {
        super(name,code);
        this.address = address;
        this.crd = crd;

    }

}

5 . 点进去源码 , 源码对该注解的解释 (不敢恭维) , 笔者感觉官方写的有些模糊.......

 总的来说  这个注解 @EqualsAndHashCode(callSuper = true) 的作用就是自动的给model bean实现equals方法和hashcode方法。 

三 : 验证 callSuper 属性

至于为什么 "callSuper = true"  , 下面笔者就针对 "callSuper = true" 和 "callSuper = false" 的两种不同情况实验下 我们还将 "callSuper" 的值 改为 false 

首先我们来看下 父类编译后的 equals方法  和 hashcode , 里面均出现了两个属性 , 一个是code , 一个 name  (笔者已标记出来)

public boolean equals(final Object o) {
        if (o == this) {
            return true;
        } else if (!(o instanceof Father01)) {
            return false;
        } else {
            Father01 other = (Father01)o;
            if (!other.canEqual(this)) {
                return false;
            } else if (this.getName() != other.getName()) { // name属性
                return false;
            } else {
                Object this$code = this.getCode(); // code 属性
                Object other$code = other.getCode();
                if (this$code == null) {
                    if (other$code != null) {
                        return false;
                    }
                } else if (!this$code.equals(other$code)) {
                    return false;
                }

                return true;
            }
        }
    }

    protected boolean canEqual(final Object other) {
        return other instanceof Father01;
    }

    public int hashCode() {
        int PRIME = true;
        int result = 1;
        int result = result * 59 + this.getName(); // neme 属性
        Object $code = this.getCode(); // code 属性
        result = result * 59 + ($code == null ? 43 : $code.hashCode());
        return result;
    }

然后我们来看 子类的 编译后类文件  , 从继承角度讲  Father01 有的属性,Child01 也是有的。请注意编译后的文件

请注意:   hashCode 方法里有一句话  result = 1 

    public boolean equals(final Object o) {
        if (o == this) {
            return true;
        } else if (!(o instanceof Child01)) {
            return false;
        } else {
            Child01 other = (Child01)o;
            if (!other.canEqual(this)) {
                return false;
            } else {
                Object this$address = this.getAddress(); // address 属性
                Object other$address = other.getAddress();
                if (this$address == null) {
                    if (other$address != null) {
                        return false;
                    }
                } else if (!this$address.equals(other$address)) {
                    return false;
                }

                Object this$crd = this.getCrd(); // crd 属性
                Object other$crd = other.getCrd();
                if (this$crd == null) {
                    if (other$crd != null) {
                        return false;
                    }
                } else if (!this$crd.equals(other$crd)) {
                    return false;
                }

                return true;
            }
        }
    }

    protected boolean canEqual(final Object other) {
        return other instanceof Child01;
    }

    public int hashCode() {
        int PRIME = true;
        int result = 1; // 注意这里  result = 1
        Object $address = this.getAddress(); // address 属性
        int result = result * 59 + ($address == null ? 43 : $address.hashCode());
        Object $crd = this.getCrd(); //  crd 属性
        result = result * 59 + ($crd == null ? 43 : $crd.hashCode());
        return result;
    }

这样问题就很明显了   ,   当子类 继承 父类时 , 虽然等于拥有了 父类的相关属性 , 但是  equals 和 hashCode 方法在进行比较时候 , 并没有对父类的相关属性纳入比较范围 ,就会出现以下问题 :

Lombok 简介 以及 Lombok 的 @EqualsAndHashCode(callSuper = false) 使用说明_第2张图片

是不是很意外 ?  这个因为在子类 Child01 里面使用@EqualsAndHashCode(callSuper = false) ,所以在进行

child01.equals(child02)

时并不调用父类的属性,那么子类属性里面的相同的话,那hashcode的值就相同啦,所以代码里面的2个 Child01 的equals方法的返回值是true

然后为了验证结果正确性 , 再将  @EqualsAndHashCode(callSuper = false)  改为 @EqualsAndHashCode(callSuper = true)

public boolean equals(final Object o) {
        if (o == this) {
            return true;
        } else if (!(o instanceof Child01)) {
            return false;
        } else {
            Child01 other = (Child01)o;
            if (!other.canEqual(this)) {
                return false;
            } else if (!super.equals(o)) {
                return false;
            } else {
                Object this$address = this.getAddress();
                Object other$address = other.getAddress();
                if (this$address == null) {
                    if (other$address != null) {
                        return false;
                    }
                } else if (!this$address.equals(other$address)) {
                    return false;
                }

                Object this$crd = this.getCrd();
                Object other$crd = other.getCrd();
                if (this$crd == null) {
                    if (other$crd != null) {
                        return false;
                    }
                } else if (!this$crd.equals(other$crd)) {
                    return false;
                }

                return true;
            }
        }
    }

    protected boolean canEqual(final Object other) {
        return other instanceof Child01;
    }

    public int hashCode() {
        int PRIME = true;
        int result = super.hashCode(); 
// 注意这里 在 @EaualsAndHashCode(... false)时候这里并未调用父类 的hashCode() , 
// 当 @EqualsAndHashCode(... true) 时候 这里编译后的类 在hashcode 之前调用了 父类的hashCode() , 
// 也就是在比较两个子类之前 , 优先比较父类
        Object $address = this.getAddress();
        result = result * 59 + ($address == null ? 43 : $address.hashCode());
        Object $crd = this.getCrd();
        result = result * 59 + ($crd == null ? 43 : $crd.hashCode());
        return result;
    }

再次运行 , 查看结果 , 如下

Lombok 简介 以及 Lombok 的 @EqualsAndHashCode(callSuper = false) 使用说明_第3张图片

 至此就是 有关于  Lombok 在 重写 equals 和 hashCode 时候 会出现的问题 以及解决办法

你可能感兴趣的:(Lombok)