Lombok @Data 注解:一键生成代码

更新时间 2023-07-29 13:24:52

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

欢迎加入小哈的星球 ,你将获得:专属的项目实战 / Java 学习路线 / 一对一提问 / 学习打卡 / 赠书活动

目前, 星球 内第一个项目:全栈前后端分离博客项目,演示地址:http://116.62.199.48/, 1.0 版本已经更新完毕,正在更新 2.0 版本。采用技术栈 Spring Boot + Mybatis Plus + Vue 3.x + Vite 4手把手,前端 + 后端全栈开发,从 0 到 1 讲解每个功能点开发步骤,1v1 答疑,陪伴式直到项目上线,目前已更新了 224 小节,累计 35w+ 字,讲解图:1544 张,还在持续爆肝中,后续还会上新更多项目,目标是将 Java 领域典型的项目都整上,如秒杀系统、在线商城、IM 即时通讯、权限管理等等,已有 1000+ 小伙伴加入,欢迎点击围观

一 、什么是 Lombok @Data 注解?

在 Lombok 中,@Data 是个万金油注解。一个 @Data 注解相当于同时使用了 @Getter@Setter@RequiredArgsConstructor@ToString@EqualsAndHashCode

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 注解会告诉 IDE (如 IntellJ、Eclipse) ,或者你的构建工具(如 Maven、Ant),在代码的编译期间为你静默生成相关样板代码。

四、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 是一个被广泛应用于生产环境的成熟工具,它可以帮助开发者生成实体类通用的一些样板代码,从而帮助我们节省大量的开发时间,还没有使用它的小伙伴们,可以赶快安排上了。