Lombok @Data 注解:快速生成类常用方法

一则或许对你有用的小广告

欢迎加入小哈的星球 ,你将获得:专属的项目实战 / 1v1 提问 / Java 学习路线 / 学习打卡 / 每月赠书 / 社群讨论

  • 新项目:《从零手撸:仿小红书(微服务架构)》 正在持续爆肝中,基于 Spring Cloud Alibaba + Spring Boot 3.x + JDK 17...点击查看项目介绍
  • 《从零手撸:前后端分离博客项目(全栈开发)》 2 期已完结,演示链接: http://116.62.199.48/

截止目前, 星球 内专栏累计输出 66w+ 字,讲解图 2896+ 张,还在持续爆肝中.. 后续还会上新更多项目,目标是将 Java 领域典型的项目都整一波,如秒杀系统, 在线商城, IM 即时通讯,权限管理,Spring Cloud Alibaba 微服务等等,已有 2300+ 小伙伴加入学习 ,欢迎点击围观

一 、什么是 Lombok @Data 注解?

在 Lombok 中,@Data 是一个集合注解,它整合了多个 Lombok 注解,用于快速生成实体类对象所需的常用方法。

一个 @Data 注解相当于同时使用了如下注解 :

  • @Getter@Setter:为每个字段生成 gettersetter 方法;

  • @ToString:生成 toString() 方法,打印对象的所有字段;

  • @EqualsAndHashCode:生成 equals()hashCode() 方法,基于字段值实现对象比较;

  • @RequiredArgsConstructor:生成包含 final@NonNull 字段的构造方法;

Lombok @Data 注解Lombok @Data 注解

二、对比编译后的源码

先来看看下面这两段 Lombok 注解使用示例:

@Getter
@Setter
@RequiredArgsConstructor
@ToString
@EqualsAndHashCode
public class User1 {
    private Long id;
    private String username;
    // 更多字段省略 ...
}
@Data
public class User2 {
    private Long id;
    private String username;
    // 更多字段省略 ...
}

编译代码,看看 Lombok 为这两个实体类生成的代码:

User1.java :

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

package com.quanxiaoha.coursefrontend.model;

public class User1 {
    private Long id;
    private String username;

    public Long getId() {
        return this.id;
    }

    public String getUsername() {
        return this.username;
    }

    public void setId(final Long id) {
        this.id = id;
    }

    public void setUsername(final String username) {
        this.username = username;
    }

    public User1() {
    }

    public String toString() {
        return "User1(id=" + this.getId() + ", username=" + this.getUsername() + ")";
    }

    public boolean equals(final Object o) {
        if (o == this) {
            return true;
        } else if (!(o instanceof User1)) {
            return false;
        } else {
            User1 other = (User1)o;
            if (!other.canEqual(this)) {
                return false;
            } else {
                Object this$id = this.getId();
                Object other$id = other.getId();
                if (this$id == null) {
                    if (other$id != null) {
                        return false;
                    }
                } else if (!this$id.equals(other$id)) {
                    return false;
                }

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

                return true;
            }
        }
    }

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

    public int hashCode() {
        int PRIME = true;
        int result = 1;
        Object $id = this.getId();
        result = result * 59 + ($id == null ? 43 : $id.hashCode());
        Object $username = this.getUsername();
        result = result * 59 + ($username == null ? 43 : $username.hashCode());
        return result;
    }
}

User2.java :

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

package com.quanxiaoha.coursefrontend.model;

public class User2 {
    private Long id;
    private String username;

    public User2() {
    }

    public Long getId() {
        return this.id;
    }

    public String getUsername() {
        return this.username;
    }

    public void setId(final Long id) {
        this.id = id;
    }

    public void setUsername(final String username) {
        this.username = username;
    }

    public boolean equals(final Object o) {
        if (o == this) {
            return true;
        } else if (!(o instanceof User2)) {
            return false;
        } else {
            User2 other = (User2)o;
            if (!other.canEqual(this)) {
                return false;
            } else {
                Object this$id = this.getId();
                Object other$id = other.getId();
                if (this$id == null) {
                    if (other$id != null) {
                        return false;
                    }
                } else if (!this$id.equals(other$id)) {
                    return false;
                }

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

                return true;
            }
        }
    }

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

    public int hashCode() {
        int PRIME = true;
        int result = 1;
        Object $id = this.getId();
        result = result * 59 + ($id == null ? 43 : $id.hashCode());
        Object $username = this.getUsername();
        result = result * 59 + ($username == null ? 43 : $username.hashCode());
        return result;
    }

    public String toString() {
        return "User2(id=" + this.getId() + ", username=" + this.getUsername() + ")";
    }
}

通过对比,会发现两个类生成的代码一致,包含:

  • 生成了所有字段的 get/set 方法;
  • 实体类的无参构造器;
  • toString() 方法;
  • hashCode() 方法;
  • equals() 方法;

三、Lombok @Data 注解是何时工作的?

Lombok @Data 注解会在代码的编译阶段,为你静默生成相关样板代码。

四、staticConstructor 参数

先看一段示例代码:

@Data(staticConstructor = "create")
public class User2 {
    private Long id;
    private String username;
    // 更多字段省略 ...
}

如果你为 @Data 注解指定了 staticConstructor 参数,再次编译代码,看看实际生成的代码:

Lombok staticConstructorLombok staticConstructor

由图可知,@Data(staticConstructor = "create") 生成了一个名为 create() 静态实体类生成方法,同时私有化了无参构造器。

五、Lombok @Data 排除指定字段

@Data 注解生成的模板方法中,默认会带上所有字段,如果针对某个字段不想生成 get/set 方法,或者是 toString()equals()hashCode() 方法排除某个字段的判断,示例代码如下:

@Data
public class User3 {
	// 不生成此字段的 set 方法
    @Setter(value = AccessLevel.NONE)
    private Long id;
    
    // 不生成此字段的 get 方法
    @Getter(value = AccessLevel.NONE)
    private String username;
    
    // toString() 方法中不打印此字段
    @ToString.Exclude
    private String email;
    
    // 1. equals() 和 hashCode() 方法中排除此字段的判断
    // 2. toString() 方法中不打印此字段
    @EqualsAndHashCode.Exclude
    @ToString.Exclude
    private Integer sex;
}

六、关于 Lombok 参数构造器

如果仅添加 @Data 注解,默认只会生成一个无参的构造器;当同时添加 @Data@Builder 注解时,仅会生成一个全参构造器。小哈在日常项目中,通常会在实体类上额外添加 @AllArgsConstructor@NoArgsConstructor 注解, 示例代码如下:

@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class User4 {
    private Long id;
    private String username;

    // 更多字段省略 ...
}

这样,该实体类就基本拥有了常用的一些模板方法,包括 builder 构造器模式、无参构造器、全参构造器等。

七、结语

本小节中,我们学习了 Lombok 的 @Data 注解,以及通过简单的代码示例,知道了如何在项目中使用它。总之,Lombok 是一个被广泛应用于生产环境的成熟工具,它可以帮助开发者生成实体类通用的一些样板代码,从而帮助我们节省大量的开发时间,还没有使用它的小伙伴们,可以赶快安排上了。