使用Lombok神级插件简化代码
更新: 12/31/2025, 6:43:00 AM 字数: 0 字 时长: 0 分钟
GitHub | Lombok最新版本 | Lombok功能 | 安装教程
简介
Lombok插件是一种 Java 实用工具,通过注解的形式,可以帮助开发人员精简代码,尤其是对于简单的 Java 对象(POJO)。推荐使用的Lombok注解如下:
@Builder
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@ToString(callSuper=true)
@EqualsAndHashCode(callSuper=true)
public class User {
}插件安装
首先,需要在IntelliJ IDEA安装Lombok插件,安装教程参考这里。然后,在pom中引入lombok依赖。
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.36</version>
</dependency>组合注解
@Getter和@Setter
(1)在类上注释
@Getter
@Setter(AccessLevel.PROTECTED)
public class User{
private String name;
}等价于 Java 源码:
public class User {
private String name;
public String getName() {
return this.name;
}
protected void setName(final String name) {
this.name = name;
}
}(2)在属性上注释
public class User{
@Getter @Setter(AccessLevel.PROTECTED)
private String name;
}等价于 Java 源码:
public class User {
private String name;
public String getName() {
return this.name;
}
protected void setName(String name) {
this.name = name;
}
}@ToString
(1)重写toString方法
// callSuper: 是是否包含父类的属性,默认为false
@ToString(callSuper=true)
public class User extends Person{
private String name;
private String age;
}等价于 Java 源码:
public class User extends Person{
private String name;
private String age;
@Override
public String toString() {
return "Demo02.User(super=" + super.toString() //// [!code ++]
+ ", name=" + this.name + ")";
}
}(2)排除指定字段
// 可以排除指定字段不参与toString()的判断
// 示例一:排除指定字段
@ToString(exclude="age")
public class User extends Person{
private String name;
private String age;
}
// 示例二:排除多个字段
@ToString(exclude={"address","age"})
public class User extends Person{
private String name;
private String age;
}
// 示例三:排除指定字段
public class User extends Person{
@ToString.Exclude private String name;
private String age;
}@EqualsAndHashCode
(1)重新equals和hashCode方法
// callSuper: 判断是是否包含父类的属性,默认为false
@EqualsAndHashCode(callSuper=true)
public class User extends Person{
private String name;
private String age;
}等价于 Java 源码
public class User extends Person {
private String name;
private String age;
private String level;
@Override
public boolean equals(final Object o) {
if (o == this) {
return true;
} else if (!(o instanceof User)) {
return false;
} else {
User other = (User)o;
if (!this instanceof User;) {
return false;
} else if (!super.equals(o)) {
return false;
} else {
// 判断name是否相同
Object this$name = this.name;
Object other$name = other.name;
if (this$name == null) {
if (other$name != null) {
return false;
}
} else if (!this$name.equals(other$name)) {
return false;
}
// 判断level是否相同
Object this$level = this.level;
Object other$level = other.level;
if (this$level == null) {
if (other$level != null) {
return false;
}
} else if (!this$level.equals(other$level)) {
return false;
}
return true;
}
}
}
@Override
public int hashCode() {
int PRIME = 59;
int result = super.hashCode();
result = result * PRIME + (this.name == null ? 43 : $name.hashCode());
result = result * PRIME + (this.level == null ? 43 : $level.hashCode());
return result;
}
}(2)排除指定字段
可以排除指定字段不参与equal()、hashCode()的判断。
// 示例一:排除指定字段
@EqualsAndHashCode(exclude="age")
public class User extends Person{
private String name;
private String age;
}
// 示例二:排除多个字段
@EqualsAndHashCode(exclude={"address","age"})
public class User extends Person{
private String name;
private String age;
}
// 示例三:排除指定字段
public class User extends Person{
@EqualsAndHashCode.Exclude private String name;
private String age;
}@Data
(1)在类上注释
使用@Data这一个注解等价于@Getter 、@Setter、@ToString、@EqualsAndHashCode、@RequiredArgsConstructor的集合。
@Data
public class Company {
private final Person founder;
private String name;
private List<Person> employees;
}等价于 Java 源码
public class User {
private String name;
public String getName() {
return this.name;
}
public void setName(final String name) {
this.name = name;
}
@Override
public boolean equals(final Object o) {
if (o == this) {
return true;
} else if (!(o instanceof User)) {
return false;
} else {
User other = (User)o;
if (!this instanceof User) {
return false;
} else {
Object this$name = this.getName();
Object other$name = other.getName();
if (this$name == null) {
if (other$name != null) {
return false;
}
} else if (!this$name.equals(other$name)) {
return false;
}
return true;
}
}
}
@Override
public int hashCode() {
int PRIME = true;
int result = 1;
Object $name = this.getName();
result = result * 59 + ($name == null ? 43 : $name.hashCode());
return result;
}
@Override
public String toString() {
return "User(name=" + this.getName() + ")";
}
}(2)staticConstructor参数
// staticConstructor可以生成一个静态的构造参数
// 但是也不允许使用new User()构造对象了
@Data(staticConstructor="of")
public class User {
private String name;
}
// 调用示例
val userName = User.of().getName();
// 提示错误
new User();等价于 Java 源码:
public class User {
private String name;
public static User of() {
return new User();
}
// 其他的省略...
}构造方法
@AllArgsConstructor
@AllArgsConstructor
public class User {
private String name;
private String age;
private String ss;
}等价于 Java 源码:
public class User {
private String name;
private String age;
public User(final String name, final String age, final String ss) {
this.name = name;
this.age = age;
}
}@NoArgsConstructor
@NoArgsConstructor
public class User {
private String name;
private String age;
private String ss;
}等价于 Java 源码:
public class User {
private String name;
private String age;
public User() {
}
}@RequiredArgsConstructor
(1)根据@NonNull标记的属性生成对应的构造方法
@RequiredArgsConstructor
public class User {
@NonNull private String name;
private String age;
private String ss;
}等价于 Java 源码:
public class User {
private @NonNull String name;
private String age;
private String ss;
public User(final @NonNull String name) {
if (name == null) {
throw new NullPointerException("name is marked non-null but is null");
} else {
this.name = name;
}
}
}(2)staticName参数
// staticName可以生成一个静态的构造参数,但是也不允许使用new User()构造对象了
@RequiredArgsConstructor(staticName="of")
public class User {
private String name;
}
// 调用示例
val userName = User.of().setName("张三");
// 提示错误
new User();等价于 Java 源码:
public class User {
private String name;
public static User of() {
return new User();
}
// 其他的省略...
}更多的注解
@val和@var
//val 定义final变量
val name = "张三";
// var 定义非final变量
var age = 14;等价于 Java 源码:
final String name = "张三";
String age = 14;@Builder
@Data、@Builder、@NoArgsConstructor、@AllArgsConstructor四个注解一般是一起使用的。@Builder.Default用于设置默认值;使用@Singular注解可以对集合字段进行赋值,但集合字段名称必须以 s 结尾。
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class User {
@Builder.Default
private Integer age = 10;
private String name;
/**
* 使用@Singular标注的参数名必须以 s 结尾
* */
@Singular
private List<String> friends;
public static void main(String[] args) {
val user1 = User.builder()
.name("张三")
.friend("李四")
.friend("王五")
.build();
System.out.println(user1.toString());
val user2 = new User();
System.out.println(user2);
}
}
// 输出:
User(age=10, name=张三, friends=[李四, 王五])
User(age=10, name=null, friends=null)@Cleanup
@Cleanup会从注解的下一行开始生成一个try-finally关闭IO流。@Cleanup("close") 的参数列表可以指定finally调用的方法,默认是"close"。
public void testCleanUp() {
try {
@Cleanup ByteArrayOutputStream baos = new ByteArrayOutputStream();
baos.write(new byte[] {'Y','e','s'});
System.out.println(baos.toString());
} catch (IOException e) {
e.printStackTrace();
}
}等价的 Java 源码:
public void testCleanUp() {
try {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
try {
baos.write(new byte[]{'Y', 'e', 's'});
System.out.println(baos.toString());
}
finally {
baos.close();// 可以指定调用的方法
}
} catch (IOException e) {
e.printStackTrace();
}
}@SneakyThrows
(1)指定抛出异常
@SneakyThrows(IllegalAccessException.class)
public void testSneakyThrows() {
throw new IllegalAccessException();
}等价的 Java 源码
public void testSneakyThrows() {
try {
throw new IllegalAccessException();
} catch (IllegalAccessException var2) {
throw var2;{
}
}(2)不指定抛出异常(即捕获所有异常)
@SneakyThrows
public void testSneakyThrows() {
throw new IllegalAccessException();
}等价的 Java 源码:
public void testSneakyThrows() {
try {
throw new IllegalAccessException();
} catch (java.lang.Throwable $ex) {
throw lombok.Lombok.sneakyThrow($ex);
}
}@Synchronized
(1)不指定锁名
@Synchronized
public String synchronizedFormat() {
return "张三";
}等价的 Java 源码:
// Lombok生成了一个锁
private final Object $lock = new Object[0];
public String synchronizedFormat() {
synchronized(this.$lock) {
return "张三";
}
}
}(2)指定锁名
private final Object readLock = new Object();
@Synchronized("readLock")
public String synchronizedFormat() {
return "张三";
}等价的 Java 源码
// 使用的是自定义的锁
private final Object readLock = new Object();
public String synchronizedFormat() {
synchronized(this.readLock) {
return "张三";
}
}@With
用于final修饰的属性前,一旦修改该final属性,就会生成一个新对象。
@Data
public class User {
@With
private final Integer no;
public static void main(String[] args) {
val user = new User(21);
val newUSer= user.withNo(21);
// 显示true,因为未修改
System.out.println(newUSer == user);
val newUSer= user.withNo(12);
// 显示false,因为已修改
System.out.println(newUSer == user);
}
}等价于 Java 源码:
public class User {
private final Integer no;
public User withNo(final Integer no) {
return this.no == no ? this : new User(no);{
}
}@NonNull
(1)在形参上使用
public void setName(@NonNull String name) {
this.name = name;
}等价于 Java 源码:
public void setName(@NonNull String name) {
if (name == null) {
throw new NullPointerException("name is marked non-null but is null");
}
else {
this.name = name;
}
}(2)在属性上使用
public class User{
@Setter @NonNull
private String name;
}等价于 Java 源码:
public class User {
private String name;
public void setName(@NonNull String name) {
if (name == null) {
throw new NullPointerException("name is marked non-null but is null");
}
else {
this.name = name;
}
}
}@Log
@Log是JDK自带的日志组件,可直接使用。若想使用@Slf4j还需要添加如下依赖:
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
<version>2.0.3</version>
</dependency>@Value
(1)用法说明
- 会使得类变成final的 (即类变得不可变 且 不能被继承)
- 会使得没有声明访问权限的属性变为私有的
- 会使得属性变为final的,可以通过@NonFinal来标记某个属性不变成final
- 同时还会生成setter()、 getter()、 equals()、hashCode()、 toString()方法
- 还会生成一个全参数的构造方法(2)用法示例
@Value
public class User {
String name;
@NonFinal protected Integer age;
}等价的 Java 源码
public final class User {
private final String name;
protected Integer age;
public User(final String name, final Integer age) {
this.name = name;
this.age = age;
}
public String getName() {
return this.name;
}
public Integer getAge() {
return this.age;
}
@Override
public boolean equals(final Object o) {
if (o == this) {
return true;
} else if (!(o instanceof User)) {
return false;
} else {
User other = (User)o;
Object this$age = this.getAge();
Object other$age = other.getAge();
if (this$age == null) {
if (other$age != null) {
return false;
}
} else if (!this$age.equals(other$age)) {
return false;
}
Object this$name = this.getName();
Object other$name = other.getName();
if (this$name == null) {
if (other$name != null) {
return false;
}
} else if (!this$name.equals(other$name)) {
return false;
}
return true;
}
}
@Override
public int hashCode() {
int PRIME = true;
int result = 1;
Object $age = this.getAge();
result = result * 59 + ($age == null ? 43 : $age.hashCode());
Object $name = this.getName();
result = result * 59 + ($name == null ? 43 : $name.hashCode());
return result;
}
@Override
public String toString() {
String var10000 = this.getName();
return "User(name=" + var10000 + ", age=" + this.getAge() + ")";
}
}注意的问题
谨慎使用@Builder
若只在类上标注了 @Data 和 @Builder 注解的时候,编译时,lombok 优化后的 Class 中会没有默认的构造方法。
在反序列化的时候,没有默认构造方法就可能会报错,这里容易造成容易忽略的BUG。实例如下所示:
@Data
@Builder
public class BuilderDemo01 {
private String name;
public static void main(String[] args) throws JsonProcessingException {
BuilderDemo01 demo01 = BuilderDemo01.builder().name("demo01").build();
ObjectMapper mapper = new ObjectMapper();
String json = mapper.writeValueAsString(demo01);
BuilderDemo01 expectDemo01 = mapper.readValue(json, BuilderDemo01.class);
System.out.println(expectDemo01.toString());
}
}@Data注解和继承
当Java子类和父类同时使用@Data注解时,可能导致equals()和toString())不正确,因为在默认情况下@EqualsAndHashCode和@ToString不会考虑父类属性。实例如下所示:
// Person类
@Data
public class Person {
private String no;
}
// User类
@Data
public class User extends Person{
private String name;
}
// 测试程序
public static void main(String[] args) {
val user1 = new User();
user1.setName("张三");
user1.setNo("2");
val user2 = new User();
user2.setName("张三");
user2.setNo("1");
// user1和user2并不相等,但此时打印的是true
System.out.println(user1.equals(user2));
} 修复此问题的方法很简单,只需使用 @Data时,在子类中添加如下注解,确保在比较时包含父类属性,以实现对称的equals方法。
@Data
@EqualsAndHashCode(callSuper=true)
public class User extends Person{
private String name;
} 然后,在观察生成的User.class文件,发现加入callSuper=true参数后,就也会判定父类的属性是否equal了。

再次执行程序,发现打印的就是false了,问题已解决。
另外,除上述方法外,还可以使用 @Getter 、@Setter 、@ToString代替@Data,并且自定义equals()方法和hashCode()方法。