Lombok 常用注解

Lombok 是一个流行的 Java 库,它通过注解的方式为开发者提供了一种简化 Java 代码的方法。它提供了多种注解,用于生成常见的样板代码,如 getter/setter 方法、构造方法、toString 方法、equals 和 hashCode 方法等。使用 Lombok 可以显著减少代码量,提高开发效率。

@NonNull

@NonNull 注解会在方法或构造函数的参数上添加非空检查。如果参数已经明确标注了其他非空注解(如 @NotNull),则 @NonNull 不会重复添加非空检查。

🏷 版本

@NonNull在 Lombok v0.11.10 中引入。

📋 概述

@NonNull可以用在组件、方法或构造函数的参数上,Lombok 将据此生成一个非空检查语句。

Lombok 将根据字段上的各种 @NonNull 注解,在生成方法或构造函数时添加非空检查,例如@Data。但是,在参数上使用 Lombok 自己的注解 @lombok.NonNull 会导致在该方法的顶部插入非空检查。

空检查看起来像if (param == null) throw new NullPointerException("param is marked non-null but is null");语句被插入到方法的最顶部。对于构造函数,将在this()/super()调用之后立即插入空检查。

如果已经存在非空判断,则不会生成额外的空检查。

原生写法

public class NonNullExample extends Something {
  private String name;

  public NonNullExample(@NonNull Person person) {
    super("Hello");
    if (person == null) {
      throw new NullPointerException("person is marked non-null but is null");
    }
    this.name = person.getName();
  }
}

Lombok 简化

import lombok.NonNull;

public class NonNullExample extends Something {
  private String name;

  public NonNullExample(@NonNull Person person) {
    super("Hello");
    this.name = person.getName();
  }
}

🛠 配置

lombok.nonNull.exceptionType= [ NullPointerException| IllegalArgumentException| JDK| Guava| Assertion]

默认:NullPointerException。当 Lombok 生成 null-check 判断语句时,默认情况下java.lang.NullPointerException会抛出“field name is located non-null but is null”的异常消息。但是,您可以在这里配置为 IllegalArgumentException,让 Lombok 使用此消息抛出该异常。如果配置为 Assertion,assert将生成具有相同信息的语句。如果配置为JDK,则提示消息格式为 java.util.Objects.requireNonNull([field name here], "[field name here] is marked non-null but is null");。如果配置为Guava,则提示消息格式为 com.google.common.base.Preconditions.checkNotNull([field name here], "[field name here] is marked non-null but is null");

lombok.nonNull.flagUsage= [ warning| error](默认:未设置)

默认:未设置。如果配置的话,Lombok 会将使用@NonNull标记为警告或错误。

🔔 说明

Lombok 对已经存在的空检查的检测方案包括扫描与 Lombok 自己的类似的 if 语句或 assert 语句:if 语句的“then”部分包含“throws”语句,无论是否在大括号中;对任何命名为 requireNonNullcheckNotNull的方法。 if 语句的条件必须与PARAMNAME == null完全相同。 assert 语句必须与 PARAMNAME != null完全相同。requireNonNull方法的调用必须是独占的(仅被该方法调用),或者必须是赋值或变量声明语句的表达式。方法中第一个语句不满足上面的条件,就认为当前方法不存在非空检查,即 Lombok 自动生成非空检查。

基本数据类型的参数上的 @NonNull 会导致警告,不会生成空检查。

在之前,@NonNull用于抽象方法的参数上会生成警告;从版本 1.16.8 开始,情况不再如此,@NonNull也具有记录作用的概念。出于同样的原因,也可以在方法上添加 @NonNull注解,这是允许的,不会生成警告,并且不会生成任何代码。

@Getter/@Setter

用于字段生成 getter/setter 方法。

📋 概述

可以在任何字段上使用 @Getter/@Setter 用于生成 getter/setter 方法。

假如有一个字段名称为 foo,默认情况下将生成方法名 getFoo 的 getter 方法(如果字段类型为 boolean 则生成 isFoo),方法名为 setFoo 的 setter 方法,其返回值为 void,并且包含一个与字段类型相同的参数。

默认情况下生成的方法访问修饰符为 public,可以通过 AccessLevel 修改为 PUBLIC 、 PROTECTED 、 PACKAGE 和 PRIVATE 。

也可以在类上添加 @Getter/@Setter 注解,其作用相当于给该类中的所有非静态字段添加了 @Getter/@Setter 注解。此时,可以通过 AccessLevel.NONE 来禁止某个字段生成 getter/setter 方法。

如果想在生成的方法上添加注解,可以使用 onMethod=@__({@AnnotationsHere}),如果想给生成的 setter 方法中参数添加注解,可以使用 onParam=@__({@AnnotationsHere})。不过,该功能目前为实验室功能。

如果在字段上有 javadoc,将会被复制到 getter/setter 方法上:@return 中的内容会被复制到 getter 方法上,@param 中的内容会被复制到 setter 上。还可以定制 setter 方法上的注释内容:在独立的一行中,用超过两个短横线 - 包裹 SETTER 单词。定制 getter 方法上的注释内容:在独立的一行中,用超过两个短横线 - 包裹 GETTER 单词。

原生写法

public class GetterSetterExample {
  /**
   * Age of the person. Water is wet.
   */
  private int age = 10;

  /**
   * Name of the person.
   */
  private String name;

  @Override public String toString() {
    return String.format("%s (age: %d)", name, age);
  }

  /**
   * Age of the person. Water is wet.
   *
   * @return The current value of this person's age. Circles are round.
   */
  public int getAge() {
    return age;
  }

  /**
   * Age of the person. Water is wet.
   *
   * @param age New value for this person's age. Sky is blue.
   */
  public void setAge(int age) {
    this.age = age;
  }

  /**
   * Changes the name of this person.
   *
   * @param name The new value.
   */
  protected void setName(String name) {
    this.name = name;
  }
}

Lombok 简化

import lombok.AccessLevel;
import lombok.Getter;
import lombok.Setter;

public class GetterSetterExample {
  /**
   * Age of the person. Water is wet.
   * 
   * @param age New value for this person's age. Sky is blue.
   * @return The current value of this person's age. Circles are round.
   */
  @Getter @Setter private int age = 10;

  /**
   * Name of the person.
   * -- SETTER --
   * Changes the name of this person.
   * 
   * @param name The new value.
   */
  @Setter(AccessLevel.PROTECTED) private String name;

  @Override public String toString() {
    return String.format("%s (age: %d)", name, age);
  }
}

🛠 配置

lombok.accessors.chain = [true | false]

默认:false。如果设置为 true ,生成的 setter 将返回 this (而不是 void)。显式配置 chain 的 @Accessors 注解参数优先于此设置。

lombok.accessors.fluent = [ true | false ]

默认:false。如果设置为 true ,则生成的 getter 和 setter 不会以 get/isset开头;这些方法将使用与字段相同的名称(减去前缀)。显式配置 fluent 的 @Accessors 注解参数优先于此设置。

lombok.accessors.prefix += 字段前缀

默认:空列表。这是一个列表属性;可以使用 += 运算符添加条目。可以使用 -= 运算符删除从父配置文件中继承的前缀。Lombok 将从字段名称中去除任何匹配的字段前缀,以确定要生成的 getter/setter 的名称。例如,如果 m 是此设置中列出的前缀之一,则名为 mFoobar 的字段将生成的 getter 方法名为 getFoobar() 而不是 getMFoobar()。显式配置 prefix 的 @Accessors 注解参数优先于此设置。

lombok.getter.noIsPrefix = [ true | false ]

默认:false。如果设置为 true ,则为 boolean 字段生成的 getter 将使用 get 前缀而不是默认 is 前缀,并且任何调用 getter 的生成代码(如 @ToString )也将使用 get 而不是 is

lombok.accessors.capitalization = [ basic | beanspec ]

默认:basic。有些字段为一个小写字母后跟一个大写,如uShaped,默认生成的 getter 方法为 getUShaped,参数设置为 beanspec则会生成 getuShaped

lombok.setter.flagUsage = [ warning | error ]

默认:未设置。如果配置的话,Lombok 会将使用@Setter标记为警告或错误。

lombok.getter.flagUsage = [ warning | error ]

默认:未设置。如果配置的话,Lombok 会将使用@Getter标记为警告或错误。

lombok.copyableAnnotations = [完全限定类型的列表]

默认值:空列表。配置以后,Lombok 会将这些注解复制到 setter 参数和 getter 方法。

🔔 说明

生成方法时,会将字段的第一个字段转换为大写,并添加 get/isset 前缀。

如果已存在具有相同名称(不区分大小写)和相同参数计数的任何方法,则不会生成任何方法。

对于 boolean 类型字段以 is 开头,生成的 getter 方法将不带任何前缀。

对于 java.lang.Boolean 类型得到的是 get 前缀,而不是 is 前缀。

来自常用库的许多指示非 null 的注解(例如 javax.annotation.Nonnull),则会导致生成的 setter 中出现显式 null 检查。

常见的非空注解(例如 org.eclipse.jdt.annotation.NonNull),会自动复制到正确的位置(getter 的方法,setter 的参数)。

使用 AccessLevel.NONE 访问级别不会产生任何结果。它仅与 @Data 或 类范围 @Getter 的 或 @Setter 结合使用时才有用。

@Getter 也可以用于枚举,@Setter 不能。

@ToString

在类上使用 @ToString 注解,可以让 Lombok 生成 toString() 方法。

📋 概述

生成的格式是固定的:类名后跟括号,其中包含以逗号分隔的字段,例如 MyClass(foo=123, bar=234)

通过将 includeFieldNames 参数设置为 true,可以让 toString() 方法在字符串表示中包含字段名称。

默认情况下,将打印所有非静态字段。如果要跳过某些字段,可以使用 @ToString.Exclude 注解来排除某些字段。或者,也可以使用 @ToString(onlyExplicitlyIncluded = true) 来准确指定要使用的字段,然后用 @ToString.Include 标记要包含的每个字段。

通过将 callSuper 设置为true,可以将父类实现的 toString 结果包含在输出中。

还可以将方法调用的输出包含在 toString 中,不过只能包含不带参数的实例(非静态)方法。为此,请用 @ToString.Include 标记该方法。

使用 @ToString.Include(name = "some other name")可以更改成员的名称,也可以使用 @ToString.Include(rank = -1)更改成员的打印顺序。没有等级的成员被视为等级为 0,等级较高的成员首先打印,相同等级的成员按照它们在源文件中显示的顺序打印。

原生写法

import java.util.Arrays;

public class ToStringExample {
  private static final int STATIC_VAR = 10;
  private String name;
  private Shape shape = new Square(5, 10);
  private String[] tags;
  private int id;

  public String getName() {
    return this.name;
  }

  public static class Square extends Shape {
    private final int width, height;

    public Square(int width, int height) {
      this.width = width;
      this.height = height;
    }

    @Override public String toString() {
      return "Square(super=" + super.toString() + ", width=" + this.width + ", height=" + this.height + ")";
    }
  }

  @Override public String toString() {
    return "ToStringExample(" + this.getName() + ", " + this.shape + ", " + Arrays.deepToString(this.tags) + ")";
  }
}

Lombok 简化

import lombok.ToString;

@ToString
public class ToStringExample {
  private static final int STATIC_VAR = 10;
  private String name;
  private Shape shape = new Square(5, 10);
  private String[] tags;
  @ToString.Exclude private int id;

  public String getName() {
    return this.name;
  }

  @ToString(callSuper=true, includeFieldNames=true)
  public static class Square extends Shape {
    private final int width, height;

    public Square(int width, int height) {
      this.width = width;
      this.height = height;
    }
  }
}

🛠 配置

lombok.toString.includeFieldNames = [ true | false ]

默认:true。通常,lombok 会以 fieldName = fieldValue 的形式为每个字段生成 toString 文本。如果此设置设置为 false ,lombok 将省略字段的名称,而只是生成以逗号分隔的所有字段值的列表。如果显式指定了注解参数 includeFieldNames,则优先于此设置。

lombok.toString.doNotUseGetters = [ true | false ]

默认:false。如果设置为 true ,则lombok在生成 toString 方法时将直接访问字段,而不是使用 getter(如果可用)。如果显式指定了注解参数 doNotUseGetters,则优先于此设置。

lombok.toString.callSuper = [ call | skip | warn ]

默认:skip。如果设置为 call ,lombok toString 将生成对父类实现的调用。如果设置为 skip “不生成”,则不会生成父类的调用。如果设置为 warn也不会生成父类调用,但 Lombok 会生成警告信息。

lombok.toString.onlyExplicitlyIncluded = [true | false]

默认:false。默认所有字段(除非 static,名称以&开头字段)都将要包含在 toString 中,可使用 @ToString.Exclude 或者 @ToString.Include 选项进行修改。如果设置为 true,则不包含任何内容,除非显式标有 @ToString.Include。

lombok.toString.flagUsage = [warning | error]

默认:未设置。如果配置的话,Lombok 会将使用@ToString标记为警告或错误。

🔔 说明

如果已经存在无参数的 toString(),则不再生成方法,而是发出警告,指出 @ToString 注解没有执行任何操作。

数组是通过 Arrays.deepToString 打印的,这意味着包含自身的数组将产生 StackOverflowError。

如果方法被标记为包含,并且它与字段同名,则该方法将替换该字段的 toString 输出,也就是在输出该字段的地方用方法代替。

在 Lombok 1.16.22 之前,可以使用 @ToString 注解的 ofexclude 参数进行包含/排除。这种旧式的包含机制仍然受支持,但将来会被弃用。

在成员上同时使用 @ToString.Exclude@ToString.Include 会生成警告;在这种情况下,该成员将被排除在外。

Lombok 不承诺在不同版本之间保持生成 toString() 方法的输出相同。

默认情况下,将自动排除任何以 $ 符号开头的变量。如果一定需要,可以使用 @ToString.Include 注解来包含它们。

如果要包含的字段存在 getter 方法,则调用该 getter 方法而不是使用直接字段引用。可以使用@ToString(doNotUseGetters = true)强制使用字段而不是 getter 方法。

@ToString 也可用于枚举定义。

如果已通过 lombok.configlombok.addNullAnnotations 配置了非空注解,则方法或返回类型将会添加非空注解。

@EqualsAndHashCode

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

🏷 版本

Lombok 0.10 中的新功能:除非你的类是 final 并且继承了 java.lang.Object,否则 Lombok 会生成一个 canEqual 方法,这意味着 JPA 代理仍然可以等于它们的基类,但添加新状态的子类不会破坏相等条件。如果需要编写自己的 equals 方法,则在更改 equals 和 hashCode 时应始终重写 canEqual 方法。

Lombok 1.14.0 中的新功能:要对 equals (或 canEqual ) 方法的 other 参数添加注解,可以使用 onParam=@__({@AnnotationsHere})。不过要小心,这是一项实验性功能。

Lombok 1.18.16 中的新功能:可以通过设置为 cacheStrategy 以外的 CacheStrategy.NEVER 值来缓存生成 hashCode() 的结果。如果可以以任何方式的变动从而导致 hashCode() 更改结果,请不要使用它。

📋 概述

@EqualsAndHashCode 注解用于让 Lombok 生成 equals(Object other) 和 hashCode() 方法的实现。默认情况下,它将使用所有非 static、非 transient 的字段,也可以通过用 @EqualsAndHashCode.Include@EqualsAndHashCode.Exclude 标记类型成员来修改使用的字段。或者,可以通过用 @EqualsAndHashCode.Include 字段或方法并使用 @EqualsAndHashCode(onlyExplicitlyIncluded = true) 来准确指定要使用的字段或方法。

当使用 @EqualsAndHashCode 注解的类继承自另一个类时,就需要注意了。父类中的字段也应该有 equals/hashCode 代码。通过设置为 callSuper=true,可以在生成的方法中包含父类的 equals 和 hashCode 方法。此时,super.hashCode()的结果将包含在哈希算法中,而对于 equals ,如果父类实现认为它不等于传入的对象,则生成的方法将返回 false。

如果你有一个显式的父类(extends 某个类),你就不得不提供 callSuper 参数来明确你已经考虑过了该情形;否则将出现警告。如果没有显示的父类(extends 某个类),设置参数 callSuper=true 将会导致编译错误。

如果想覆盖某个字段的 equals 行为,可以在字段上添加注解 @EqualsAndHashCode.Include(replaces = “fieldName”)。然后在定义一个方法:@EqualsAndHashCode.Include BigDecimal fieldName() { return fieldName.stripTrailingZeros(); }

原生写法

import java.util.Arrays;

public class EqualsAndHashCodeExample {
  private transient int transientVar = 10;
  private String name;
  private double score;
  private Shape shape = new Square(5, 10);
  private String[] tags;
  private int id;

  public String getName() {
    return this.name;
  }

  @Override 
  public boolean equals(Object o) {
    if (o == this) return true;
    if (!(o instanceof EqualsAndHashCodeExample)) return false;
    EqualsAndHashCodeExample other = (EqualsAndHashCodeExample) o;
    if (!other.canEqual((Object)this)) return false;
    if (this.getName() == null ? other.getName() != null : !this.getName().equals(other.getName())) return false;
    if (Double.compare(this.score, other.score) != 0) return false;
    if (!Arrays.deepEquals(this.tags, other.tags)) return false;
    return true;
  }

  @Override 
  public int hashCode() {
    final int PRIME = 59;
    int result = 1;
    final long temp1 = Double.doubleToLongBits(this.score);
    result = (result*PRIME) + (this.name == null ? 43 : this.name.hashCode());
    result = (result*PRIME) + (int)(temp1 ^ (temp1 >>> 32));
    result = (result*PRIME) + Arrays.deepHashCode(this.tags);
    return result;
  }

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

  public static class Square extends Shape {
    private final int width, height;

    public Square(int width, int height) {
      this.width = width;
      this.height = height;
    }

    @Override 
    public boolean equals(Object o) {
      if (o == this) return true;
      if (!(o instanceof Square)) return false;
      Square other = (Square) o;
      if (!other.canEqual((Object)this)) return false;
      if (!super.equals(o)) return false;
      if (this.width != other.width) return false;
      if (this.height != other.height) return false;
      return true;
    }

    @Override 
    public int hashCode() {
      final int PRIME = 59;
      int result = 1;
      result = (result*PRIME) + super.hashCode();
      result = (result*PRIME) + this.width;
      result = (result*PRIME) + this.height;
      return result;
    }

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

Lombok 简化

import lombok.EqualsAndHashCode;

@EqualsAndHashCode
public class EqualsAndHashCodeExample {
  private transient int transientVar = 10;
  private String name;
  private double score;
  @EqualsAndHashCode.Exclude 
  private Shape shape = new Square(5, 10);
  private String[] tags;
  @EqualsAndHashCode.Exclude 
  private int id;

  public String getName() {
    return this.name;
  }

  @EqualsAndHashCode(callSuper=true)
  public static class Square extends Shape {
    private final int width, height;

    public Square(int width, int height) {
      this.width = width;
      this.height = height;
    }
  }
}

🛠 配置

lombok.equalsAndHashCode.doNotUseGetters = [true | false]

默认:false。如果设置为 true ,lombok 将直接访问字段,而不是在生成 equals 和 hashCode 方法时使用 getter 方法。如果显式指定了注解参数 doNotUseGetters,则优先于此设置。

lombok.equalsAndHashCode.callSuper = [call | skip | warn]

默认:warn。如果设置为 call ,lombok 将生成会调用父类实现的 hashCode 和 equals 方法。如果设置为 skip “不生成”,则不会生成此类调用。默认行为类似于 skip ,并带有附加警告。

lombok.equalsAndHashCode.flagUsage = [warning | error]

默认:未设置。如果配置的话,Lombok 会将使用@EqualsAndHashCode标记为警告或错误。

🔔 说明

如果一个数组包含自身引用,可能会产生 StackOverflowError 。不过 ArrayList 不会有该问题。

目前,hashCode 的实现在各个版本之间差异不大,不过如果后面出现一种显著提高性能的哈希算法,有可能会在未来替换成该算法。

目前,double 中的 NaN 和 float 中的 NaN 被认为是相等的。

尝试排除 static 字段,或 transient 字段,或不存在的字段将会出现警告。

如果方法被标记为包含,并且该方法与字段具有相同的名称,则该方法将替换该字段。

在 Lombok 1.16.22 之前,可以使用 @EqualsAndHashCode 注释的 of 和 exclude 参数进行包含/排除。这种旧式的包含机制仍然受支持,但将来将被弃用。

默认情况下,将自动排除任何以 $ 符号开头的变量,只能通过用 @EqualsAndHashCode.Include 将其手动包含进来。

如果要包含的字段存在 getter 方法,则调用该 getter 方法而不是使用直接字段引用。可以通过参数阻止此行为:@EqualsAndHashCode(doNotUseGetters = true)

@NoArgsConstructor/@RequiredArgsConstructor/@AllArgsConstructor

这三个注解都是用来生成构造方法的。

📋 概述

@NoArgsConstructor 将生成一个没有参数的构造函数。如果类中包含 final 修饰的字段,将会编译错误,此时,可以使用 @NoArgsConstructor(force = true) 强制将 final 字段初始化为 0 / false / null 。如果有字段被标记为非空字段(添加了 @NonNull等)将不会被校验,因为无法满足非空条件。某些框架(如 Hibernate /SPI 等)需要无参构造函数,该注解通常与 @Data 注解或其他有参构造的注解结合使用。

@RequiredArgsConstructor 将生成一个有参构造函数:所有未初始化的 final 字段以及任何标记为 @NonNull 未初始化的字段都会获得一个参数。对于标有 @NonNull 的字段,还会生成显式 null 检查,如果任何参数用于标记为 @NonNull 的字段为 null,则构造函数将抛出NullPointerException。参数的顺序与字段在类中的出现顺序相同。

@AllArgsConstructor 将生成一个全参构造函数。标有 @NonNull 的字段将触发对这些参数进行 null 检查。

这三个注解都可以使用别名来简写,此时,生成的构造方法将是私有 private 的,外界可以通过一个静态方法访问内部私有的构造函数。该功能通过参数 staticName 启用:例如有一个这样的注解 @RequiredArgsConstructor(staticName="of"),意味着可以使用 MapEntry.of("foo", 5)初始化对象,而不是更长的 new MapEntry<String, Integer>("foo", 5)

要在生成的构造函数上添加注释,可以使用 onConstructor=@__({@AnnotationsHere}) ,但要小心,这是一个实验性功能。

这三个注解会跳过静态字段。

如果已经自己写好了构造函数,上面的注解还是会根据自身功能生成相应的构造函数。如果发生冲突(自己定义的构造函数与 Lombok 生成的构造函数有相同的签名),则将发生编译器错误。

原生写法

public class ConstructorExample<T> {
  private int x, y;
  @NonNull private T description;

  private ConstructorExample(T description) {
    if (description == null) throw new NullPointerException("description");
    this.description = description;
  }

  public static <T> ConstructorExample<T> of(T description) {
    return new ConstructorExample<T>(description);
  }

  @java.beans.ConstructorProperties({"x", "y", "description"})
  protected ConstructorExample(int x, int y, T description) {
    if (description == null) throw new NullPointerException("description");
    this.x = x;
    this.y = y;
    this.description = description;
  }

  public static class NoArgsExample {
    @NonNull private String field;

    public NoArgsExample() {
    }
  }
}

Lombok 简化

import lombok.AccessLevel;
import lombok.RequiredArgsConstructor;
import lombok.AllArgsConstructor;
import lombok.NonNull;

@RequiredArgsConstructor(staticName = "of")
@AllArgsConstructor(access = AccessLevel.PROTECTED)
public class ConstructorExample<T> {
  private int x, y;
  @NonNull private T description;

  @NoArgsConstructor
  public static class NoArgsExample {
    @NonNull private String field;
  }
}

🛠 配置

lombok.allArgsConstructor.flagUsage = [warning | error]

默认:未设置。如果配置的话,Lombok 会将使用@AllArgsConstructor标记为警告或错误。

lombok.requiredArgsConstructor.flagUsage = [warning | error]

默认:未设置。如果配置的话,Lombok 会将使用@RequiredArgsConstructor标记为警告或错误。

lombok.noArgsConstructor.flagUsage = [warning | error]

默认:未设置。如果配置的话,Lombok 会将使用@NoArgsConstructor标记为警告或错误。

lombok.anyConstructor.flagUsage = [warning | error]

默认:未设置。如果配置的话,Lombok 会将使用上述三个注解中任意一个都标记为警告或错误。

lombok.anyConstructor.addConstructorProperties = [true | false]

默认:false。如果设置为 true,则 Lombok 会将 @java.beans.ConstructorProperties 添加到生成的构造函数中。

lombok.copyableAnnotations = [完全限定类型的列表]

Lombok 将会把该列表中指定的注解复制到构造器的参数、Setter 参数、getter 方法上。

lombok.noArgsConstructor.extraPrivate = [true | false]

默认:false。如果 true ,lombok 将为所有带 @Value 或 @Data 注解的类生成一个私有的无参构造函数,该构造函数将所有字段设置为默认值 (null / 0 / false)。

🔔 说明

即使字段显式初始化为 null,lombok 也不会将 null 视为满足要求,并且不会将该字段视为“必需”参数。

无参构造函数不能添加 @java.beans.ConstructorProperties注解,这也解释了为什么 @NoArgsConstructor 缺少 suppressConstructorProperties 参数。生成的静态工厂方法也不会得到 @ConstructorProperties ,因为这个注解只能添加到真正的构造函数中。

这三个注解可以用于枚举。生成的构造函数将始终是私有的,因为非私有构造函数在枚举中是不合法的。您不必指定 AccessLevel.PRIVATE

如果构造函数是由 @Data@Value 注解生成的,则 flagUsage 参数将不生效。

@Data

@Data 注解包含了 @ToString,@EqualsAndHashCode,@Getter,@Setter,@RequiredArgsConstructor。此外, @Data 注解同时会生成一个无参构造函数,除非类中存在 final 字段。

📋 概述

@Data 是一个组合注解,它包含了 @ToString@EqualsAndHashCode@Getter@Setter@RequiredArgsConstructor。添加了 @Data 注解,意味着该类所有字段包含了 getter/setter方法、toString方法、equals方法、hashCode 方法,以及初始化所有非空字段的构造函数(如果有final 字段会初始化)。

@Data 注解虽然包含了多个注解的特征,但是这些注解的参数(如 callSupper、exclude等)确不能在 @Data 中设置,如果想设置这些参数,需要显示添加特定注解。

所有生成的 getter 和 setter 都将是 public 。若要覆盖访问级别,请使用显式设置 @Setter 或 @Getter。

生成的hashCode 和 equals 方法将不考虑所有标记为transient的字段,所有静态字段都将被完全跳过。

如果将要生成方法在类中已经显示存在,则不会生成该方法,并且不会发出警告或错误。例如,如果您已经有一个签名为 equals(AnyType param) 的方法,则不会生成任何 equals 方法。

@Data 可以很好地处理字段的泛型参数。

原生写法

import java.util.Arrays;

public class DataExample {
  private final String name;
  private int age;
  private double score;
  private String[] tags;

  public DataExample(String name) {
    this.name = name;
  }

  public String getName() {
    return this.name;
  }

  void setAge(int age) {
    this.age = age;
  }

  public int getAge() {
    return this.age;
  }

  public void setScore(double score) {
    this.score = score;
  }

  public double getScore() {
    return this.score;
  }

  public String[] getTags() {
    return this.tags;
  }

  public void setTags(String[] tags) {
    this.tags = tags;
  }

  @Override public String toString() {
    return "DataExample(" + this.getName() + ", " + this.getAge() + ", " + this.getScore() + ", " + Arrays.deepToString(this.getTags()) + ")";
  }

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

  @Override public boolean equals(Object o) {
    if (o == this) return true;
    if (!(o instanceof DataExample)) return false;
    DataExample other = (DataExample) o;
    if (!other.canEqual((Object)this)) return false;
    if (this.getName() == null ? other.getName() != null : !this.getName().equals(other.getName())) return false;
    if (this.getAge() != other.getAge()) return false;
    if (Double.compare(this.getScore(), other.getScore()) != 0) return false;
    if (!Arrays.deepEquals(this.getTags(), other.getTags())) return false;
    return true;
  }

  @Override public int hashCode() {
    final int PRIME = 59;
    int result = 1;
    final long temp1 = Double.doubleToLongBits(this.getScore());
    result = (result*PRIME) + (this.getName() == null ? 43 : this.getName().hashCode());
    result = (result*PRIME) + this.getAge();
    result = (result*PRIME) + (int)(temp1 ^ (temp1 >>> 32));
    result = (result*PRIME) + Arrays.deepHashCode(this.getTags());
    return result;
  }

  public static class Exercise<T> {
    private final String name;
    private final T value;

    private Exercise(String name, T value) {
      this.name = name;
      this.value = value;
    }

    public static <T> Exercise<T> of(String name, T value) {
      return new Exercise<T>(name, value);
    }

    public String getName() {
      return this.name;
    }

    public T getValue() {
      return this.value;
    }

    @Override public String toString() {
      return "Exercise(name=" + this.getName() + ", value=" + this.getValue() + ")";
    }

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

    @Override public boolean equals(Object o) {
      if (o == this) return true;
      if (!(o instanceof Exercise)) return false;
      Exercise<?> other = (Exercise<?>) o;
      if (!other.canEqual((Object)this)) return false;
      if (this.getName() == null ? other.getValue() != null : !this.getName().equals(other.getName())) return false;
      if (this.getValue() == null ? other.getValue() != null : !this.getValue().equals(other.getValue())) return false;
      return true;
    }

    @Override public int hashCode() {
      final int PRIME = 59;
      int result = 1;
      result = (result*PRIME) + (this.getName() == null ? 43 : this.getName().hashCode());
      result = (result*PRIME) + (this.getValue() == null ? 43 : this.getValue().hashCode());
      return result;
    }
  }
}

Lombok 简化

import lombok.AccessLevel;
import lombok.Setter;
import lombok.Data;
import lombok.ToString;

@Data public class DataExample {
  private final String name;
  @Setter(AccessLevel.PACKAGE) private int age;
  private double score;
  private String[] tags;

  @ToString(includeFieldNames=true)
  @Data(staticConstructor="of")
  public static class Exercise<T> {
    private final String name;
    private final T value;
  }
}

🛠 配置

lombok.data.flagUsage = [warning | error]

默认:未设置。如果配置的话,Lombok 会将使用@Data标记为警告或错误。

lombok.noArgsConstructor.extraPrivate = [true | false]

默认:false。如果设置为 true,lombok 将为任何 @Data 带注释的类生成一个私有的无参构造函数,该构造函数将所有字段设置为默认值 (null / 0 / false)。

🔔 说明

生成方法的非空检查将参照 @Getter/@Setter 注解。

默认情况下,将自动排除任何以 $ 符号开头的变量。您可以通过指定显式注释(例如 @Getter 或 @ToString )并使用‘of’参数来包含它们。

@Builder

@Builder 注解用于在类上生成构建器模式的代码,允许通过链式调用的方式构建对象。

📋 概述

使用 Lombok 的 @Builder 注解可以显著减少样板代码,并使对象构建过程更加清晰和易于管理。

@Builder 相对来说会复杂些,另起一篇详细介绍。

@Log

@Log 注解系列用于生成日志对象,支持多种日志框架。

🏷 版本

Lombok v0.10 中添加了各种 @Log 变体。

Lombok v1.16.24 中的新功能:添加了谷歌的 FluentLogger(通过 @Flogger )。

Lombok v1.18.10 中的新功能: @CustomLog 添加它允许自定义 logger。

📋 概述

在 class 上添加 @Log 注解,可以获得一个 static final log 字段,该字段将按照指定的日志框架进行初始化。

@Log 注解是一系列注解的统称,它们包括:

  • @CommonsLog:private static final org.apache.commons.logging.Log log = org.apache.commons.logging.LogFactory.getLog(LogExample.class);
  • @Flogger:private static final com.google.common.flogger.FluentLogger log = com.google.common.flogger.FluentLogger.forEnclosingClass();
  • @JBossLog:private static final org.jboss.logging.Logger log = org.jboss.logging.Logger.getLogger(LogExample.class);
  • @Log:private static final java.util.logging.Logger log = java.util.logging.Logger.getLogger(LogExample.class.getName());
  • @Log4j:private static final org.apache.log4j.Logger log = org.apache.log4j.Logger.getLogger(LogExample.class);
  • @Log4j2:private static final org.apache.logging.log4j.Logger log = org.apache.logging.log4j.LogManager.getLogger(LogExample.class);
  • @Slf4j:private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(LogExample.class);
  • @XSlf4j:private static final org.slf4j.ext.XLogger log = org.slf4j.ext.XLoggerFactory.getXLogger(LogExample.class);
  • @CustomLog:private static final com.foo.your.Logger log = com.foo.your.LoggerFactory.createYourLogger(LogExample.class);

@CustomLog 主要用来支持公司私有的日志框架。使用前,需要在 lombok.config 中配置。

例如,配置为 lombok.log.custom.declaration = com.foo.your.Logger com.foo.your.LoggerFactory.createYourLog(TYPE)(TOPIC)。在等号右边首先定义日志类型,然后是一个空格,接着是日志工厂,日志实现方法名,后面是参数。参数包括: TYPE (将此 @Log 修饰类型以class形式传递)、 NAME (传递此 @Log 修饰类型的完全限定名称)、 TOPIC (传递@CustomLog注解的 topic 参数字符串)和 NULL (传递 null )。

日志类型是可选的,如果省略,则使用日志工厂类型。

默认情况下,日志的主题(或名称)将是使用 @Log 注解的类名称。也可以通过指定 topic 参数来自定义。例如: @XSlf4j(topic=”reporting”) 。

原生写法

public class LogExample {
  private static final java.util.logging.Logger log = java.util.logging.Logger.getLogger(LogExample.class.getName());

  public static void main(String... args) {
    log.severe("Something's wrong here");
  }
}

public class LogExampleOther {
  private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(LogExampleOther.class);

  public static void main(String... args) {
    log.error("Something else is wrong here");
  }
}

public class LogExampleCategory {
  private static final org.apache.commons.logging.Log log = org.apache.commons.logging.LogFactory.getLog("CounterLog");

  public static void main(String... args) {
    log.error("Calling the 'CounterLog' with a message");
  }
}

Lombok 简化

import lombok.extern.java.Log;
import lombok.extern.slf4j.Slf4j;

@Log
public class LogExample {

  public static void main(String... args) {
    log.severe("Something's wrong here");
  }
}

@Slf4j
public class LogExampleOther {

  public static void main(String... args) {
    log.error("Something else is wrong here");
  }
}

@CommonsLog(topic="CounterLog")
public class LogExampleCategory {

  public static void main(String... args) {
    log.error("Calling the 'CounterLog' with a message");
  }
}

🛠 配置

lombok.log.fieldName = 标识符

默认值:log。默认情况下,生成的日志字段名为“log”,可以使用此设置将其更改为其他名称。

lombok.log.fieldIsStatic = [ true | false ]

默认值:true。通常,生成的 log 字段是 static 字段。通过将此键设置为 false ,生成的字段将改为实例字段。

lombok.log.custom.declaration = LoggerType LoggerFactoryType.loggerFactoryMethod(loggerFactoryMethodParams)(loggerFactoryMethodParams)

当使用 @CustomLog 时需要配置的内容。

lombok.log.flagUsage = [warning | error]

默认:未设置。如果配置的话,Lombok 会将使用各种@Log标记为警告或错误。这里 @Log 是统称,指的是所有的日志注解。

lombok.log.custom.flagUsage = [warning | error]

默认:未设置。如果配置的话,Lombok 会将使用@CustomLog标记为警告或错误。

lombok.log.apacheCommons.flagUsage = [warning | error]

默认:未设置。如果配置的话,Lombok 会将使用@CommonsLog标记为警告或错误。

lombok.log.flogger.flagUsage = [warning | error]

默认:未设置。如果配置的话,Lombok 会将使用@Flogger标记为警告或错误。

lombok.log.jbosslog.flagUsage = [warning | error]

默认:未设置。如果配置的话,Lombok 会将使用@JBossLog标记为警告或错误。

lombok.log.log4j.flagUsage = [warning | error]

默认:未设置。如果配置的话,Lombok 会将使用@Log4j标记为警告或错误。

lombok.log.log4j2.flagUsage = [warning | error]

默认:未设置。如果配置的话,Lombok 会将使用@Log4j2标记为警告或错误。

lombok.log.slf4j.flagUsage = [warning | error]

默认:未设置。如果配置的话,Lombok 会将使用@Slf4j标记为警告或错误。

lombok.log.xslf4j.flagUsage = [warning | error]

默认:未设置。如果配置的话,Lombok 会将使用@XSlf4j标记为警告或错误。

🔔 说明

如果已存在一个 log 字段,则会发出警告,并且不会生成任何代码。

📝总结

使用 Lombok 时,需要注意其注解的具体用法和配置选项,以确保生成的代码符合预期。同时,由于 Lombok 生成的是编译时代码,因此在团队协作和代码维护中需要考虑到 Lombok 的使用对其他开发者的影响。

转载请注明出处:码谱记录 » Lombok 常用注解