简介

Lombok 是一个 Java 库,它可以自动插入编辑器和构建工具中,简化 Java 代码的编写。 Lombok 提供了一系列注解,用于自动生成 getter、setter、构造函数、toString 方法等,从而减少了代码量,提高了代码的可读性和可维护性。

安装

1
2
3
4
5
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.18.28</version>
</dependency>

常用注解

注解名作用
@Data自动生成 getter、setter、toString、equals、hashCode 方法
@Getter自动生成 getter 方法
@Setter自动生成 setter 方法
@ToString自动生成 toString 方法
@EqualsAndHashCode自动生成 equals 和 hashCode 方法
@NoArgsConstructor自动生成无参构造函数
@AllArgsConstructor自动生成全参构造函数

全部注解

@Data

@Data注解是一个非常强大的综合注解,它等价于同时使用@ToString、@EqualsAndHashCode、@Getter、@Setter和@RequiredArgsConstructor。使用@Data可以大大简化代码,减少样板代码的编写。

1
2
3
4
5
6
import lombok.Data;
@Data
public class Person {
    private String name;
    private int age;
}

@Getter和@Setter

1. 放置位置的灵活性

放在类上边:为所有属性生成getter和setter方法

1
2
3
4
5
6
@Getter
@Setter
public class Person {
    private String name;
    private int age;
}

放在属性上边:为该属性生成getter和setter方法

1
2
3
4
5
6
public class Person {
    @Getter
    @Setter
    private String name;
    private int age;
}

2. 控制生成的方法的行为 @Getter(lazy=true):延迟加载,只有在第一次调用getter方法时才会生成getter方法,通常用于性能优化,避免在对象创建时进行不必要的初始化操作。

1
2
3
4
public class Person {
    @Getter(lazy=true)
    private final String name = "John";
}

@Setter(AccessLevel.PROTECTED):设置setter方法的访问级别为protected

1
2
3
4
public class Person {
    @Setter(AccessLevel.PROTECTED)
    private String name;
}

AccessLevel的可选值包括:

  • PUBLIC:生成public方法(默认值)。
  • PACKAGE:生成无访问修饰符的方法(即包级私有)。
  • PROTECTED:生成protected方法。
  • PRIVATE:生成private方法。
  • MODULE:仅限模块内使用(与PACKAGE类似)。
  • NONE:不生成对应的方法,适合排除某些字段。

@Getter(onMethod=@_(@JsonIgnore)):在生成的getter方法上添加注解@JsonIgnore,用于忽略序列化。

1
2
3
4
public class Person {
    @Getter(onMethod=@_(@JsonIgnore))
    private String name; 
}

@Setter(onParam=@_(@NonNull)):在生成的setter方法的参数上添加注解@NonNull,用于校验参数非空。

1
2
3
4
public class Person {
    @Setter(onParam=@_(@NonNull))
    private String name;
}

@ToString

@ToString(exclude=“name”):排除name属性,不生成对应的getter方法。

1
2
3
4
5
@ToString(exclude="name")
public class Person {
    private String name;
    private int age;
}

@ToString(of=“name”):只包含name属性,只生成对应的getter方法。

1
2
3
4
5
@ToString(of="name")
public class Person {
    private String name;
    private int age;
}

@ToString(includeFieldNames=true):包含属性名,生成的toString方法中包含属性名。如果设置为false,则只打印字段值。

1
2
3
4
5
@ToString(includeFieldNames=true)
public class Person {
    private String name;
    private int age;
}

@ToString(callSuper=true):调用父类的toString方法。

1
2
3
4
@ToString(callSuper=true)
public class Student extends Person {
    private String school;
}

@ToString(doNotUseGetters=true):默认情况下,生成的toString方法会尽可能使用getter方法获取字段值。如果字段没有getter方法,则直接访问字段。可以通过设置doNotUseGetters = true关闭这个功能。

1
2
3
4
5
@ToString(doNotUseGetters=true)
public class Person {
    private String name;
    private int age;
}

@ToString(onlyExplicitlyIncluded=true):开启后,将只为字段或getter方法上添加了@ToString.Include注解的属性生成toString方法,类似于“白名单”模式。

1
2
3
4
5
6
@ToString(onlyExplicitlyIncluded=true)
public class Person {
    @ToString.Include
    private String name;
    private int age;
}

@EqualsAndHashCode

@EqualsAndHashCode 注解用于自动生成equals和hashCode方法。

1
2
3
4
5
@EqualsAndHashCode
public class Person {
    private String name;
    private int age;
}

@EqualsAndHashCode(exclude=“name”):排除某些字段,使其不被包含在equals和hashCode方法中。

1
2
3
4
5
@EqualsAndHashCode(exclude="name")
public class Person {
    private String name;
    private int age;
}

@EqualsAndHashCode(of=“name”):指定哪些字段需要包含在equals和hashCode方法中。默认情况下,所有 非静态字段 都会被包含。

1
2
3
4
5
@EqualsAndHashCode(of="name")
public class Person {
    private String name;
    private int age;
}

@EqualsAndHashCode(callSuper=true):调用父类的equals和hashCode方法。默认值为false。

1
2
3
4
@EqualsAndHashCode(callSuper=true)
public class Student extends Person {
    private String school;
}

@EqualsAndHashCode(doNotUseGetters=true):默认情况下,生成的equals和hashCode方法会尽可能使用getter方法获取字段值。如果字段没有getter方法,则直接访问字段。可以通过设置doNotUseGetters = true关闭这个功能。

1
2
3
4
5
@EqualsAndHashCode(doNotUseGetters=true)
public class Person {
    private String name;
    private int age;
}

生成构造函数

@NoArgsConstructor:

注解用于生成一个无参构造函数。默认情况下,如果类中存在final类型的字段,Lombok不会生成无参构造函数,因为final字段必须在构造函数中初始化。不过,可以通过force属性强制生成无参构造函数,此时Lombok会将所有字段初始化为默认值(例如,int类型初始化为0,String类型初始化为null)。

1
2
3
4
5
6
@NoArgsConstructor
// @NoArgsConstructor(force = true)
public class Person {
    private String name;
    private int age;
}

@AllArgsConstructor:

注解用于生成一个包含所有字段的构造函数。此外,还可以通过staticName属性将构造函数转换为静态工厂方法,让代码更加优雅。

1
2
3
4
5
@AllArgsConstructor
public class Person {
    private String name;
    private int age;
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
@AllArgsConstructor(staticName = "with")
public class Account {
    private String name;
    private int age;
    private int id;
}
// 生成代码:
public class Account {
    private String name;
    private int age;
    private int id;

    public Account(String name, int age, int id) {
        this.name = name;
        this.age = age;
        this.id = id;
    }

    public static Account with(String name, int age, int id) {
        return new Account(name, age, id);
    }
}
// 调用:
Account account = Account.with("小明", 18, 1);

@RequiredArgsConstructor:

注解用于生成一个构造函数,该构造函数包含所有final字段和标记为@NonNull的字段。这非常适合在需要确保某些字段在创建对象时必须被初始化的场景中使用。

1
2
3
4
5
6
7
@RequiredArgsConstructor
public class Person {
    @NonNull
    private String name;
    private final int age;
    private String address; // 非final字段和非NonNll,不会被包含在构造函数中
}

@Builder:生成一个带有Builder模式的构造函数

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
@Builder
public class Person {
    private String name;
    private int age;
}

// 试用Builder模式创建对象:
Person person = Person.builder()
        .name("John")
        .age(25)
        .build();

builderMethodName:设置生成的builder方法的名称。默认为builder。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
@Builder(builderMethodName = "create")
public class Person {
    private String name;
    private int age;
}
// 试用Builder模式创建对象:
Person person = Person.create()
       .name("John")
       .age(25)
       .build();

buildMethodName:设置生成的build方法的名称。默认为build。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
@Builder(buildMethodName = "createPerson")
public class Person {
    private String name;
    private int age;
}
// 试用Builder模式创建对象:
Person person = Person.builder()
      .name("John")
      .age(25)
      .createPerson();

toBuilder:设置是否生成toBuilder方法,用来修改某个字段。默认为false。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
@Builder(toBuilder = true)
public class Person {
    private String name;
    private int age;
}
// 试用Builder模式创建对象:
Person person = Person.builder()
     .name("John")
     .age(25)
     .build();
// 调用toBuilder方法:
Person newPerson = person.toBuilder()
    .age(30)
    .build();

setterPrefix:设置生成的setter方法的前缀。默认为set。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
@Builder(setterPrefix = "set")
public class Person {
    private String name;
    private int age;
}
// 试用Builder模式创建对象:
Person person = Person.builder()
    .setName("John")
    .setAge(25)
    .build();

val和var:类型推断

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
import java.util.ArrayList;
import java.util.HashMap;
import lombok.val;

public class ValExample {
  public String example() {
    val example = new ArrayList<String>();
    example.add("Hello, World!");
    val foo = example.get(0);
    return foo.toLowerCase();
  }
  
  public void example2() {
    val map = new HashMap<Integer, String>();
    map.put(0, "zero");
    map.put(5, "five");
    for (val entry : map.entrySet()) {
      System.out.printf("%d: %s\n", entry.getKey(), entry.getValue());
    }
  }
}

var:用于局部变量的类型推断,变量可以被重新赋值。

1
2
var list = new ArrayList<String>();
list = new LinkedList<String>(); // 可以重新赋值

val:用于局部变量的类型推断,变量不可被重新赋值,类似于final。

1
2
val list = new ArrayList<String>();
// list = new LinkedList<String>(); // 错误:不能重新赋值

@NotNull:非空检查

1
2
3
4
5
6
import lombok.NonNull;
public class NotNullExample {
  public void example(@NonNull String name) {
    System.out.println("Hello, " + name + "!");
  }
}

@Cleanup:自动关闭资源

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import lombok.Cleanup;
public class CleanupExample {
  public void copyFile(String src, String dest) throws IOException {
    @Cleanup FileInputStream in = new FileInputStream(src);
    @Cleanup FileOutputStream out = new FileOutputStream(dest);
    byte[] buffer = new byte[1024];
    int length;
    while ((length = in.read(buffer)) != -1) {
      out.write(buffer, 0, length);
    } 
  } 
}

对比原生写法:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

import java.io.*;

public class CleanupExample {
  public static void main(String[] args) throws IOException {
    InputStream in = new FileInputStream(args[0]);
    try {
      OutputStream out = new FileOutputStream(args[1]);
      try {
        byte[] b = new byte[10000];
        while (true) {
          int r = in.read(b);
          if (r == -1) break;
          out.write(b, 0, r);
        }
      } finally {
        if (out != null) {
          out.close();
        }
      }
    } finally {
      if (in != null) {
        in.close();
      }
    }
  }
}

@SneakyThrows:自动抛出异常

1
2
3
4
5
6
7
8
import lombok.SneakyThrows;

@SneakyThrows
public static void main(String[] args) {
    @Cleanup InputStream inputStream = new FileInputStream("test.txt");
    byte[] bytes = inputStream.readAllBytes();
    System.out.println(new String(bytes));
}

对比原生写法:

1
2
3
4
5
public static void main(String[] args) throws IOException {
    @Cleanup InputStream inputStream = new FileInputStream("test.txt");
    byte[] bytes = inputStream.readAllBytes();
    System.out.println(new String(bytes));
}

@Value

@Value注解是一个非常强大的注解,它可以自动生成一个不可变类,即所有字段都是final的,并且提供了getter方法,但没有setter方法。

1
2
3
4
5
@Value
public class Person {
    private String name;
    private int age;
}

@Synchronized

在多线程编程中,同步是一个重要的问题,@Synchronized注解用于生成一个同步方法,即使用synchronized关键字修饰方法。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
@Synchronized
public void increment() {
    count++;
}
@Synchronized("lock")
public void decrement() {
    count--;
}

private final Object lock = new Object();
  • @Synchronized注解只能用于方法。
  • 如果需要更复杂的同步逻辑,建议使用java.util.concurrent包中的工具类。

@Locked

@Locked注解用于生成一个同步块,即使用synchronized关键字修饰代码块。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
@Locked("lock")
public void increment() {
    synchronized (lock) {
        count++;
    }
}

private int value = 0;

@Locked.Read
public int getValue() {
return value;
}

@Locked.Write
public void setValue(int newValue) {
value = newValue;
}

@With

@With注解用于生成一个带有指定字段的拷贝方法。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
@With
public class Person {
    private String name;
    private int age;
}

// 生成代码:
public class Person {
    private String name;
    private int age;
    public Person withName(String name) {
        return new Person(name, this.age);
    }
    public Person withAge(int age) {
        return new Person(this.name, age);
    }
}

@Log 和 @Slf4j

Lombok提供了多种日志注解,如@Log、@Slf4j等注解用于生成一个日志对象,即使用java.util.logging.Logger或java.util.logging.Level类记录日志。

1
2
3
4
5
6
7
@Log
//@Slf4j
public class Person {
    public void sayHello() {
        log.info("Hello, world!");
    }
}

@Accessors

@Accessors注解用于控制生成的getter和setter方法的行为。

chain:设置是否生成链式调用。默认为false。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
@Accessors(chain = true) // 启用链式调用
public class Person {
    private String name;
    private int age;
}
// 生成代码:   
public class Person {
    private String name;
    private int age;
    public Person setName(String name) {
        this.name = name;
        return this;
    }
    public Person setAge(int age) {
        this.age = age;
        return this;
    }
}
// 使用链式调用:
Person person = new Person().setName("John").setAge(25);

fluent:设置是否生成fluent风格的getter和setter方法。默认为false。访问器方法将直接以字段名命名,而不是使用get或set前缀。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
@Accessors(fluent = true)
public class Person {
    private String name;
    private int age;
}
// 生成代码:
public class Person {
    private String name;
    private int age;
    public String getName() {
        return name;
    }
    public Person setName(String name) {
        this.name = name;
        return this;
    }
    public int getAge() {
        return age;
    }
    public Person setAge(int age) {
      
    }
}
// 使用fluent风格的调用:
Person person = new Person().name("John").age(25);

prefix:设置生成的getter和setter方法的前缀。默认为空字符串。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
@Accessors(prefix = "m")
public class Person {
    private String name;
    private int age;
}
// 生成代码:
public class Person {
    private String name;
    private int age;
    public String mName() {
        return name;
    }
    public Person mName(String name) {
        this.name = name;
        return this;
    }
    public int mAge() {
        return age;
    }
    public Person mAge(int age) {
        this.age = age;
        return this;
    }
}
// 使用fluent风格的调用:
Person person = new Person().mName("John").mAge(25);

makeFinal:设置是否生成final字段的getter方法。默认为false。如果设置为true,则生成的getter和setter方法将被标记为final,从而防止子类覆盖这些方法。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
@Accessors(makeFinal = true)
public class Person {
    private String name;
    private int age;
}
// 生成代码:
public class Person {
    private final String name;
    private final int age;
    public String getName() {
        return name;
    }
    public int getAge() {
        return age;
    }
}
// 使用fluent风格的调用:
Person person = new Person().name("John").age(25);

官网:https://projectlombok.org/

参考:https://juejin.cn/post/7470102625956413467