Lombok实验室功能

Lombok 中所有的实验性功能介绍与使用。

@Accessors

@Accessors 可以让你更流畅的使用 getter 和 setter 方法。

🏷 版本

@Accessors 在 Lombok v0.11.0 中作为实验性功能引入。

@Accessors 在 Lombok v1.18.24 中较大变化:

  • 增加了配置项:lombok.accessors.capitalization = [ basic | beanspec ]
  • 如果字段没有配置,则会从 class 上继承
  • @Accessors(makeFinal = true) 将创建 final getter、setter 和 with-ers。

🔬 实验室

该特性目前属于实验性功能主要基于以下几点考虑:

  • 希望 @Accessors 支持的功能更完整
  • 参数 makeFinal 是新加入的,还在等社区反馈

当前有以下几个点还没有定论:

  • 灵活控制访问器的命名:例如有布尔变量 boolean wasRunning 希望生成的方法是 boolean wasRunning() 而不是生成 boolean isWasRunning()
  • 字段上的 @Accessors 配置不会导致类级别的 @Accessors 生效【目前类级别的 @Accessors 配置会使所有该类中没有添加注解的字段也生效】。

📋 概述

默认情况下,lombok 遵循 bean 规范的 getter 和 setter 方法。例如,名为 pepper 的字段的 getter 方法是 getPepper 。当然,@Accessors 可以帮助你打破 bean 规范,以便最终得到更美观的 API。

@Accessors 有4个配置项:

  • fluent – 布尔值,默认值:false。如果为 true,则 pepper 的 getter 为 pepper() ,setter 为 pepper(T newValue) 。此外,除非指定,否则 chain 将为 true。
  • chain – 布尔值,默认值:false。如果为 true,则返回生成的 setter 将返回 this 而不是 void。
  • makeFinal – 布尔值,默认值:false。如果为 true,则生成的 getter、setter 和 with-ers 将被标记为 final。
  • prefix – 字符串列表。如果存在,则字段必须以这些前缀中的任意一个为前缀。一些人喜欢使用字段前缀:例如他们写 fPepper 而不是 pepper。这时就需要配置一个 f的前缀。需要注意的是,对于字母字符,前缀后面的字符不能是小写字母,即 pepper 与前缀 p 不匹配,而 pEpper 能匹配上(表示此字段的基本名称为 epper )。

@Accessors 注解可以添加在类型和字段上;getters/setters/with-ers 将首先查看字段上的注解,其次查看字段所在的类型(并且您在 types 中有类型,每个外部类型也会被检查),最后对于任何未显式设置的属性,会适当的参考 lombok.config 设置。

原生写法

public class AccessorsExample {
  private int age = 10;

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

  public AccessorsExample age(final int age) {
    this.age = age;
    return this;
  }
}

class PrefixExample {
  private String fName = "Hello, World!";

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

Lombok 简化

import lombok.experimental.Accessors;
import lombok.Getter;
import lombok.Setter;

@Accessors(fluent = true)
public class AccessorsExample {
  @Getter @Setter
  private int age = 10;
}

class PrefixExample {
  @Accessors(prefix = "f") @Getter
  private String fName = "Hello, World!";
}

🛠 配置

lombok.accessors.flagUsage = [warning | error]

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

lombok.accessors.chain = [true | false]

默认:false。如果设置为 true ,则任何没有 @Accessors 注解或有注解但没有 chain 参数显式值的字段/类都将被认为有 @Accessors(chain = true) 存在。

lombok.accessors.fluent = [true | false]

默认:false。如果设置为 true ,则任何没有 @Accessors 注解或有注解但没有 fluent 参数显式值的字段/类都将被认为有 @Accessors(fluent = true) 存在。

lombok.accessors.makeFinal = [true | false]

默认:false。如果设置为 true ,则任何没有 @Accessors 注解或有注解但没有 makeFinal 参数显式值的字段/类都将被认为有 @Accessors(makeFinal = true) 存在。

lombok.accessors.prefix += 字段前缀列表

默认:空列表。这是一个列表属性;可以使用 += 运算符添加条目。可以使用 -= 运算符删除从父配置文件中继承的前缀。如果字段没有显示配置 prefix 参数,则使用该配置。

lombok.accessors.capitalization = [basic | beanspec]

默认值:basic。如何处理一个小写字母后跟一个大写字母开头的单词:例如 uShaped,配置为 basic 将其大写为 getUShaped ,配置为 beanspec 将其写为 getuShaped。

🔔 说明

一些 Lombok 注解会使用 @Accessors 生成的 getter 方法,如 @EqualsAndHashCode。

如果提供了前缀列表,但字段不是以其中一个开头,则 lombok 将完全跳过该字段,并生成警告。

@ExtensionMethod

用于将公共的静态方法调用变得更加方便。

🏷 版本

@ExtensionMethod 在 Lombok v0.11.2 中作为实验性功能引入。

🔬 实验室

该特性目前属于实验性功能主要基于以下几点考虑:

  • 对代码风格影响很大。
  • 真的很想提供实用方法来扩展公共类,但到目前为止,lombok 还没有针对此类运行时依赖项的良好处理方法。
  • 对 eclipse 影响较大,并且自动完成 在 netbeans 中尚不起作用。
  • 不确定是否可以添加到方法上,能否添加到 package
  • 此功能出现的 bug 比较多,而且修复难度有点大

目前此功能不会很快退出实验状态,但它不会发生重大变化,并且在未来版本的 Lombok 中也不太可能删除对它的支持。

📋 概述

在一个类中包含一些 public static 且至少有一个参数的方法,使用 @ExtensionMethod 可以让这些方法看起来就像第一个参数的实例方法一样。

可以扩展的方法至少要有一个参数,且该参数类型不能是基本数据类型。

例如有一个方法 public static String toTitleCase(String in) { ... },在使用了 @ExtensionMethod 以后,就好像 java.lang.String 类具有了名为 toTitleCase 的方法,该方法没有参数,其实是把 this 当做方法的第一个参数使用。

代码的调用方式 foo.toTitleCase() 将被替换为 ClassContainingYourExtensionMethod.toTitleCase(foo); 。这样,即使 foo 为 null,也不会出现 NullPointerException。

@ExtensionMethod 可以传入多个类,这样的话,这些类中满足条件的方法都将被扩展。

Lombok 中目前没有提供共享的扩展方法,你需要根据场景自己定义静态方法。例如,当数据为空时取默认值的方法可以如下定义:

public class ObjectExtensions {
    public static <T> T or(T object, T ifNull) {
        return object != null ? object : ifNull;
    }
}

在需要使用上述方法的类上,添加注解 @ExtensionMethod(ObjectExtensions.class),然后:

String x = null;
System.out.println(x.or("Hello, World!"));

上面的代码不会出现 NullPointerException, 它实际上会输出 Hello, World!

@ExtensionMethod 注解是添加在需要使用静态方法的类上的,而不是添加到实现静态方法的类上。

原生写法

public class ExtensionMethodExample {
  public String test() {
    int[] intArray = {5, 3, 8, 2};
    java.util.Arrays.sort(intArray);

    String iAmNull = null;
    return Extensions.or(iAmNull, Extensions.toTitleCase("Hello, World!"));
  }
}

class Extensions {
  public static <T> T or(T obj, T ifNull) {
    return obj != null ? obj : ifNull;
  }

  public static String toTitleCase(String in) {
    if (in.isEmpty()) return in;
    return "" + Character.toTitleCase(in.charAt(0)) +
        in.substring(1).toLowerCase();
  }
}

Lombok 简化

import lombok.experimental.ExtensionMethod;

@ExtensionMethod({java.util.Arrays.class, Extensions.class})
public class ExtensionMethodExample {
  public String test() {
    int[] intArray = {5, 3, 8, 2};
    intArray.sort();

    String iAmNull = null;
    return iAmNull.or("Hello, World!".toTitleCase());
  }
}

class Extensions {
  public static <T> T or(T obj, T ifNull) {
    return obj != null ? obj : ifNull;
  }

  public static String toTitleCase(String in) {
    if (in.isEmpty()) return in;
    return "" + Character.toTitleCase(in.charAt(0)) +
        in.substring(1).toLowerCase();
  }
}

🛠 配置

lombok.extensionMethod.flagUsage = [warning | error]

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

🔔 说明

@ExtensionMethod 只是重写了对静态方法的调用方式,该方法需要保证在编译时和运行时都可以被访问到。

泛型中的泛型参数类型在扩展时也被考虑了。如果方法第一个的参数为 List<? extends String>,则只有 List<String> 会被识别,而 List<Object>不会识别。

@FieldDefaults

给字段添加新的默认访问修饰符。

🏷 版本

@FieldDefaults在 Lombok v0.11.4 中作为实验性功能引入。

🔬 实验室

该特性目前属于实验性功能主要基于以下几点考虑:

  • package-info.java 中可以设置该包中所有类的默认值;
  • 配置 lombok.fieldDefaults.defaultPrivate = true会影响到所有的源文件,目前还没法确定这样做是否合适。

📋 概述

@FieldDefaults 注解添加到类或者枚举上可以使其每个字段都拥有默认修饰符(public , private , 或 protected )。

使用 @FieldDefaults(makeFinal=true) 还可以给每个字段都添加 final 关键字,如果不希望某个字段添加 final ,可以在字段上添加 @NonFinal 注解。

使用 @FieldDefaults(level=AccessLevel.PRIVATE) 可以给类中每个没有访问修饰符的字段都添加 private 访问修饰符。Java 中 package 层级的访问修饰符(一般称之为 default)恰好不用写关键字,如果确定该字段应该是 default 访问修饰符,可以在该字段上添加注解 @PackagePrivate。

原生写法

public class FieldDefaultsExample {
  public final int a;
  private final int b;
  private int c;
  final int d;

  FieldDefaultsExample() {
    a = 0;
    b = 0;
    d = 0;
  }
}

Lombok 简化

import lombok.AccessLevel;
import lombok.experimental.FieldDefaults;
import lombok.experimental.NonFinal;
import lombok.experimental.PackagePrivate;

@FieldDefaults(makeFinal=true, level=AccessLevel.PRIVATE)
public class FieldDefaultsExample {
  public final int a;
  int b;
  @NonFinal int c;
  @PackagePrivate int d;

  FieldDefaultsExample() {
    a = 0;
    b = 0;
    d = 0;
  }
}

🛠 配置

lombok.fieldDefaults.flagUsage = [warning | error]

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

lombok.fieldDefaults.defaultPrivate = [true | false]

默认:false。如果设置为 true ,则正在编译的源代码中的每个类或枚举中的每个字段都将被标记为 private, 除非它具有显式访问修饰符或 @PackagePrivate 注解,或者存在显式 @FieldDefaults 注解。

lombok.fieldDefaults.defaultFinal = [true | false]

默认:false。如果设置为 true ,则正在编译的源代码中的每个类或枚举中的每个字段都将被标记为 final,除非它具有 @NonFinal 注解,或者存在显式 @FieldDefaults 注解。

🔔 说明

与其他触及字段的 lombok 处理程序一样,任何名称以美元 $ 符号开头的字段都将被完全跳过。这样的字段根本不会被修改。

@Delegate

可以让当前类使用其他类中的方法。

🏷 版本

@Delegate 在 Lombok v0.10 中作为功能引入(实验包尚不存在)。

@Delegate 在 Lombok v1.14 中作为实验性功能引入。

🔬 实验室

该特性目前属于实验性功能主要基于以下几点考虑:

  • 使用的不多;
  • 难以支持边缘情况,例如递归委托;
  • API 设计不够友好;

📋 概述

任何字段或无参数方法都可以进行 @Delegate 注释,以便 lombok 生成将调用转发到此字段(或调用此方法的结果)的委托方法。

Lombok 委托所有public方法的字段类型(或方法的返回类型),以及其父类的方法 ,但 java.lang.Object 中声明的所有方法除外。

@Delegate 的参数 types 可以接受多个类,当存在该参数时,Lombok 将委托该参数中的所有类以及其父类的 public 方法(但不包含java.lang.Object 声明的所有方法)。

@Delegate(excludes=SomeType.class) 可以用于排除不想被委托的类型。

为了非常精确地控制委托的内容和不委托的内容,可以编写带有方法签名的私有内部接口,然后将这些私有内部接口指定为 @Delegate 注解中的参数 @Delegate(types=PrivateInnerInterfaceWithIncludesList.class, excludes=SameForExcludes.class) 。

原生写法

import java.util.ArrayList;
import java.util.Collection;

public class DelegationExample {
  private interface SimpleCollection {
    boolean add(String item);
    boolean remove(Object item);
  }

  private final Collection<String> collection = new ArrayList<String>();

  @java.lang.SuppressWarnings("all")
  public boolean add(final java.lang.String item) {
    return this.collection.add(item);
  }

  @java.lang.SuppressWarnings("all")
  public boolean remove(final java.lang.Object item) {
    return this.collection.remove(item);
  }
}

class ExcludesDelegateExample {
  long counter = 0L;

  private interface Add {
    boolean add(String x);
    boolean addAll(Collection<? extends String> x);
  }

  private final Collection<String> collection = new ArrayList<String>();

  public boolean add(String item) {
    counter++;
    return collection.add(item);
  }

  public boolean addAll(Collection<? extends String> col) {
    counter += col.size();
    return collection.addAll(col);
  }

  @java.lang.SuppressWarnings("all")
  public int size() {
    return this.collection.size();
  }

  @java.lang.SuppressWarnings("all")
  public boolean isEmpty() {
    return this.collection.isEmpty();
  }

  @java.lang.SuppressWarnings("all")
  public boolean contains(final java.lang.Object arg0) {
    return this.collection.contains(arg0);
  }

  @java.lang.SuppressWarnings("all")
  public java.util.Iterator<java.lang.String> iterator() {
    return this.collection.iterator();
  }

  @java.lang.SuppressWarnings("all")
  public java.lang.Object[] toArray() {
    return this.collection.toArray();
  }

  @java.lang.SuppressWarnings("all")
  public <T extends .java.lang.Object>T[] toArray(final T[] arg0) {
    return this.collection.<T>toArray(arg0);
  }

  @java.lang.SuppressWarnings("all")
  public boolean remove(final java.lang.Object arg0) {
    return this.collection.remove(arg0);
  }

  @java.lang.SuppressWarnings("all")
  public boolean containsAll(final java.util.Collection<?> arg0) {
    return this.collection.containsAll(arg0);
  }

  @java.lang.SuppressWarnings("all")
  public boolean removeAll(final java.util.Collection<?> arg0) {
    return this.collection.removeAll(arg0);
  }

  @java.lang.SuppressWarnings("all")
  public boolean retainAll(final java.util.Collection<?> arg0) {
    return this.collection.retainAll(arg0);
  }

  @java.lang.SuppressWarnings("all")
  public void clear() {
    this.collection.clear();
  }
}

Lombok 简化

import java.util.ArrayList;
import java.util.Collection;

import lombok.experimental.Delegate;

public class DelegationExample {
  private interface SimpleCollection {
    boolean add(String item);
    boolean remove(Object item);
  }

  @Delegate(types=SimpleCollection.class)
  private final Collection<String> collection = new ArrayList<String>();
}


class ExcludesDelegateExample {
  long counter = 0L;

  private interface Add {
    boolean add(String x);
    boolean addAll(Collection<? extends String> x);
  }

  @Delegate(excludes=Add.class)
  private final Collection<String> collection = new ArrayList<String>();

  public boolean add(String item) {
    counter++;
    return collection.add(item);
  }

  public boolean addAll(Collection<? extends String> col) {
    counter += col.size();
    return collection.addAll(col);
  }
}

🛠 配置

lombok.delegate.flagUsage = [warning | error]

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

🔔 说明

参数 types 和 excludes 中的类不能包含泛型。可以通过私有的内部接口或类来实现类似的效果。

@Delegate 不能用于静态字段或方法。

@Delegate 不能嵌套使用,也就是说 @Delegate 不能用于委托包含 @Delegate 注解的类上。

onMethod/onConstructor/onParam

给注解添加注解:用在 Lombok 注解的参数中,用于给生成的方法添加注解。为了简化它们 ,下面用 onX 代替 onMethod/onConstructor/onParam 。

🏷 版本

onX在 Lombok v0.11.8 中作为实验性功能引入。

🔬 实验室

该特性目前属于实验性功能主要基于以下几点考虑:

  • 语法看起来不太美观。
  • 可能 Java 9 会提供(更)更好的方法来支持这个功能。
  • Java 的未来版本可能会破坏此功能。

📋 概述

该特性的存在,主要是来解决部分 Lombok 用户的特殊需求。如果后来有更好的办法来实现相同的功能,或者后面 Java 引入了实现该功能的机制,那可能会移除此特性。此外,该功能可能在未来的 javac 中不被支持,使用前请慎重。

大部分生成方法的 Lombok 注解都支持 onX 特性。

onMethod 可以用在 @Getter 、 @Setter 和 @Wither 注解中。

onConstructor 可以用在 @AllArgsConstructor 、 @NoArgsConstructor 和 @RequiredArgsConstructor 注解中。

onParam 可以用在 @Setter 和 @Wither 注解中, 指定的注解将放在生成的方法具有的唯一参数上。 还可以用在 @EqualsAndHashCode 上,指定的注解将作用于 equals 方法上。

在 Java7 中使用该特性,需要将注解包装在 @__(@AnnotationGoesHere) 中。要应用多个注释,请使用 @__({@Annotation1, @Annotation2})

在 Java8 及更高版本上,可以在 onMethod 、 onParam 或 onConstructor 之后添加下划线。

原生写法

import javax.inject.Inject;
import javax.persistence.Id;
import javax.persistence.Column;
import javax.validation.constraints.Max;

public class OnXExample {
  private long unid;

  @Inject
  public OnXExample(long unid) {
    this.unid = unid;
  }

  @Id @Column(name="unique-id")
  public long getUnid() {
    return unid;
  }

  public void setUnid(@Max(10000) long unid) {
    this.unid = unid;
  }
}

Lombok 简化

import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.Setter;

import javax.inject.Inject;
import javax.persistence.Id;
import javax.persistence.Column;
import javax.validation.constraints.Max;

@AllArgsConstructor(onConstructor=@__(@Inject))
public class OnXExample {
//  @Getter(onMethod=@__({@Id, @Column(name="unique-id")})) //JDK7
//  @Setter(onParam=@__(@Max(10000))) //JDK7
  @Getter(onMethod_={@Id, @Column(name="unique-id")}) //JDK8
  @Setter(onParam_=@Max(10000)) //JDK8
  private long unid;
}

🛠 配置

lombok.onX.flagUsage = [warning | error]

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

🔔 说明

该特性在 Java7 中的奇怪写法是因为如下原因:@__ 注解将指向类型 __(两个下划线),而该类型是不存在的,此时 Java7 编译停止,等待后续注解处理器 annotation processor 创建类型 __,然后 Lombok 在处理注解的时候会删除该引用,从而 Java7 正常编译。需要注意的是,类型 __ 一定要是不存在的,如果在项目中使用到类型 __或者引入的框架中有类型 __,则应该在 onX 中添加更多的下划线来避免被误删除。理论上任何不存在的类型都可以使用在 onX 上,但是为了保持一致性和防止误用,这里要求必须是一长串的下划线___来标识。

在 Java8 中,编译行为有所变化,如果注解类型中不存在参数名称,则编译将进入 Lombok 可以修复它的阶段。

重申一下:此功能随时可能消失;如果要使用此功能,请准备好在找到更好的实现此功能的方法时调整代码。

onX 必须使用在合适的位置:例如,在类上的 @Setter 注解中添加 onMethod 也不会生效。

@UtilityClass

让工具类更加实用。

🏷 版本

@UtilityClass 在 Lombok v1.16.2 中作为实验性功能引入。

🔬 实验室

该特性目前属于实验性功能主要基于以下几点考虑:

  • 关于它是否足够普遍,可以算作样板,存在一些争论。
  • 由于 Javac 的限制,静态导入可能存在问题。

📋 概述

工具类包含一系列实用方法:它不需要创建实例对象,所有成员都是静态的。常见的工具类有 java.lang.Mathjava.util.Collections等。该注解就是自动将一个类转换为工具类。

工具类不能被实例化:使用 @UtilityClass 注解后,该类中的构造函数会被私有化,类会被标记为 final。如果该类为内部类,会被标记为 static。类中所有成员都会被标记为 static ,包括字段和内部类。

原生写法

public final class UtilityClassExample {
  private static final int CONSTANT = 5;

  private UtilityClassExample() {
    throw new java.lang.UnsupportedOperationException("This is a utility class and cannot be instantiated");
  }

  public static int addSomething(int in) {
    return in + CONSTANT;
  }
}

Lombok 简化

import lombok.experimental.UtilityClass;

@UtilityClass
public class UtilityClassExample {
  private final int CONSTANT = 5;

  public int addSomething(int in) {
    return in + CONSTANT;
  }
}

🛠 配置

lombok.utilityClass.flagUsage = [warning | error]

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

🔔 说明

目前没有任何方法可以创建非静态成员或定义自己的构造函数。如果要实例化工具类,即使仅作为内部实现细节, @UtilityClass 也不能使用。

由于 javac 处理静态导入的特殊方式,尝试对“@UtilityClass”的任何成员进行非星形静态导入是行不通的。

可以使用星形静态导入:import static TypeMarkedWithUtilityClass.*;,或者不静态导入任何成员。

@Helper

扩展 Java 中的方法:在方法中传递方法。

🏷 版本

@Helper 在 Lombok v1.16.6 中作为实验性功能引入。

🔬 实验室

该特性目前属于实验性功能主要基于以下几点考虑:

  • 使用 lambda 表达式可以实现相同的效果。
  • 提供的模板能力较少,对开发的帮助有限。

📋 概述

此注释允许你将方法放入方法中。如果想将方法声明在方法中,需要在方法中先声明一个 class,在该 class 中定义的方法可以访问在此 class 之前声明的局部变量或参数。不过要想调用该 class 中的方法,需要先实例化该 class。使用了 @Helper 注解后,可以不用实例化 class 了,该 class 中的方法可以直接调用。

要实例化方法中定义的 class,需要使用到 HelperClass h = new HelperClass();,然后使用 h.helperMethod(); 调用此 class 中的方法。有了 @Helper 注解后,可以直接方法 class 中定义的方法 helperMethod();

原生写法

public class HelperExample {
  int someMethod(int arg1) {
    int localVar = 5;

    class Helpers {
      int helperMethod(int arg) {
        return arg + localVar;
      }
    }
    Helpers $Helpers = new Helpers();

    return $Helpers.helperMethod(10);
  }
}

Lombok 简化

import lombok.experimental.Helper;

public class HelperExample {
  int someMethod(int arg1) {
    int localVar = 5;

    @Helper class Helpers {
      int helperMethod(int arg) {
        return arg + localVar;
      }
    }

    return helperMethod(10);
  }
}

🛠 配置

lombok.helper.flagUsage = [warning | error]

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

🔔 说明

@Helper 生成的是无参构造函数创建实例。如果不是这种情形为编译报错。

如果方法中的 class 名为 Foo,则生成的对象为 $Foo,不过不应该在程序中使用该变量,因为有可能会在未来有改变。

请不要在方法中的 class 里使用 this 关键字,这将导致混乱。

如果在方法中的 class 中定义的方法名在外部有相同的方法签名,则默认调用的是 class 中的方法。

如果在 JDK7 及以下版本中使用,则 class 中方法使用到的局部变量和参数需要使用 final 关键字修饰,这是 Java 的限制。

@FieldNameConstants

可以让该类中的字段拥有一个与字段名相同的字符串常量。

🏷 版本

@FieldNameConstants在 Lombok v1.16.22 中作为实验性功能引入。

@FieldNameConstants在 Lombok v1.18.4 中进行了重新设计。

🔬 实验室

该特性目前属于实验性功能主要基于以下几点考虑:

  • 不确定是否对代码有破坏性。
  • 作为刚刚推出的功能,仍在收集反馈。

📋 概述

@FieldNameConstants 会生成一个内部类,该内部类包含外部类种每个静态 final 字符串字段的一个常量。

通过 @FieldNameConstants(asEnum = true) 可以生成内部枚举类。

通过 lombok.fieldNameConstants.uppercase = true 可以让生成字段转为大写。

默认情况下,生成的内部类声明为 public static final class Fields,可以通过 @FieldNameConstants(innerTypeName = "FieldNames", level = AccessLevel.PACKAGE)来修改类名和访问修饰符。无论类的访问修饰符如何,生成的字段永远是 public 的。

@FieldNameConstants 只能用在类上,默认情况下,所有没标记 transient,没标记 static 的字段都会被处理,也可以通过 @FieldNameConstants.Include + @FieldNameConstants(onlyExplicitlyIncluded = true),或者使用 @FieldNameConstants.Exclude 来精确控制需要处理的字段。

原生写法

public class FieldNameConstantsExample {
  private final String iAmAField;
  private final int andSoAmI;
  private final int asAmI;

  public static final class Fields {
    public static final String iAmAField = "iAmAField";
    public static final String andSoAmI = "andSoAmI";
  }
}

Lombok 简化

import lombok.experimental.FieldNameConstants;
import lombok.AccessLevel;

@FieldNameConstants
public class FieldNameConstantsExample {
  private final String iAmAField;
  private final int andSoAmI;
  @FieldNameConstants.Exclude private final int asAmI;
}

🛠 配置

lombok.fieldNameConstants.flagUsage = [warning | error]

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

lombok.fieldNameConstants.innerTypeName = a string

默认:Fields。通过该配置修改内部类型的名称。

lombok.fieldNameConstants.uppercase = [ true | false ]

默认:false。如果配置为 true ,会将字段转换为大写。

🔔 说明

从 lombok v1.18.6 开始,可以自己定义内部 Fields 枚举/类,在这种情况下,lombok 将添加所有未编写的枚举常量/公共静态字段。

在lombok版本 v1.16.22 到 v1.18.2,生成的字段在当前类中,并且添加了前缀 FIELD_。从 lombok v1.18.4 开始,生成的字段移动到了内部类中。

任何名称以美元 ( $ ) 符号开头的字段都将被完全跳过。静态字段也会被跳过。

如果要在 MapStruct 中使用生成的字段,需要手动在 @Mapping 中指明:@Mappings({@Mapping(target = Entity.Fields.entityProperty, source = "dtoProperty")})

@SuperBuilder

@SuperBuilder 注解会为你的类生成复杂的构建器。

🏷 版本

@SuperBuilder 在 Lombok v1.18.2 中作为实验性功能引入。

@SuperBuilder 的 toBuilder 的功能和自定义支持是在 Lombok v1.18.4 中添加的。

📋 概述

@SuperBuilder 注解用于给类生成复杂的 builder API。与 @Builder 不同,@SuperBuilder 对于父类中的字段也生效。此外,它还要求父类也添加 @SuperBuilder 注解。

使用了 @SuperBuilder 之后,可以通过如下方式实例化对象:

Person.builder().name("Adam Savage").city("San Francisco").job("Mythbusters").job("Unchained Reaction").build();

@SuperBuilder 与 @Builder 不兼容,不能同时使用。

使用 @SuperBuilder(toBuilder = true) 可以在类中创建一个 toBuilder() 方法,该方法用于创建一个 builder ,如果使用该参数,需要父类也使用此参数。

在字段上使用 @Builder.ObtainVia 用于替代实例化时,该字段的取值逻辑。例如,可以这样使用 @Builder.ObtainVia(method = "calculateFoo")

为了保证类型安全,在 Foobar 类上使用 @SuperBuilder 之后,会生成抽象类 FoobarBuilder 和具体实现类 FoobarBuilderImpl 。

当前支持的参数:

  • build() 方法的名称(默认值: “build” )
  • builder() 方法的名称(默认值: “builder” )
  • 是否需要 toBuilder() (默认值:否)
  • 是否让 setter 方法包含 set 前缀,默认是 Person.builder().name("Jane").build(),可以设置为 Person.builder().setName("Jane").build()

所有参数都定制的样例:

@SuperBuilder(buildMethodName = "execute", builderMethodName = "helloWorld", toBuilder = true, setterPrefix = "set")

🛠 配置

lombok.superBuilder.flagUsage = [warning | error]

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

lombok.builder.className = [带有可选星号的 Java 标识符,用于指示返回类型名称的位置]

默认:*Builder。生成的构建器类的名称,其中 *表示当前类名。

lombok.singular.useGuava = [ true | false ]

默认:false。使用采用 Guava 的 ImmutableXxx 来替代 java.util 中的 Collections.unmodifiableXxx,如果配置为 true,需要保证项目中引入了 Guava 依赖。

lombok.singular.auto = [ true | false ]

默认:true。是否让 Lombok 猜测字段的单数形式,设置为 false ,则需要指明字段的单数形式,如果不指明会出现错误。

@Tolerate

在方法或者构造函数上添加该注解,让 Lombok 认为该方法/构造函数不存在。

🏷 版本

@Tolerate在 Lombok v1.14.2 中作为实验性功能引入。

🔬 实验室

该特性目前属于实验性功能主要基于以下几点考虑:

  • 使用的场景不多。
  • 难以支持边缘情况,例如递归委托。

📋 概述

可以在方法或者构造函数上使用 @Tolerate 注解,用于对 Lombok “隐藏”该方法。例如有一个名为 date 的字段被添加了 @Setter 注解,此时 Lombok 应该生成一个 setDate()方法,但是如果在类中存在一个 setDate()方法,则不会生成该方法。 要是在该 setDate() 方法上添加了 @Tolerate 注解,则会生成一个 setDateXX() 方法。

原生写法

public class TolerateExample {
    @Setter
    private Date date;

    public void setDateFromString(String date) {
        this.date = Date.valueOf(date);
    }
}

Lombok 简化

import lombok.experimental.Tolerate;

public class TolerateExample {
    @Setter
    private Date date;

    @Tolerate
    public void setDate(String date) {
        this.date = Date.valueOf(date);
    }
}

@Jacksonized

让使用 Jackson 变得更简单。

🏷 版本

@Jacksonized 在 Lombok v1.18.14 中作为实验性功能引入。

📋 概述

注解 @Jacksonized 是 @SuperBuilder 和 @Builder 的附加注释。它会自动将生成的构建器类配置为供 Jackson 的反序列化使用。它只有在存在 @Builder 或 @SuperBuilder 注解时才有效果;否则会发出警告。

如果没有 @Jacksonized ,则必须自定义构建器类。

Lombok 简化

@Jacksonized @Builder
@JsonIgnoreProperties(ignoreUnknown = true)
public class JacksonExample {
    private List<Foo> foos;
}

🔔 说明

如果类名为 Foobar,@SuperBuilder 注解生成的类后缀为 Impl,则使用 @Jacksonized 注解后,会在生成的类上添加 Jackson 注解 @JsonDeserialize(builder=Foobar.FoobarBuilder[Impl].class)),要是该注解已经存在了,则会报错。

将于Jackson 相关的注解复制到生成的 builder 类上。

如果使用了 Lombok 参数 setterPrefix,则其值会被添加到注解 @JsonPOJOBuilder(withPrefix="")中。

@StandardException

用于简化自定义异常。

🏷 版本

@StandardException 在 Lombok v1.18.21 中作为实验性功能引入。

📋 概述

在自定义异常上使用 @StandardException,可以帮助你生成如下 4 个构造函数:

  • 无参构造函数 MyException()
  • 仅包含 message 的构造函数 MyException(String message)
  • 仅包含 cause 的构造函数 MyException(Throwable cause)
  • 完整构造函数,包含 message 和 cause MyException(String message, Throwable cause)

如果类中已经存在相同声明的构造函数,则不会生成该函数。

原生写法

public class ExampleException extends Exception {
    public ExampleException() {
        this(null, null);
    }

    public ExampleException(String message) {
        this(message, null);
    }

    public ExampleException(Throwable cause) {
        this(cause != null ? cause.getMessage() : null, cause);
    }

    public ExampleException(String message, Throwable cause) {
        super(message);
        if (cause != null) super.initCause(cause);
    }
}

Lombok 简化

import lombok.experimental.StandardException;

@StandardException
public class ExampleException extends Exception {
}

🛠 配置

lombok.standardException.flagUsage = [warning | error]

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

lombok.standardException.addConstructorProperties = [true | false]

默认:false。

🔔 说明

Lombok 不会检查此 class 是否继承了真正的异常类。

Lombok 不要求此 class 继承了 Throwable ,但是要求继承的类必须有一个无参构造函数和一个字符串类型参数的构造函数。

一般来说,使用 new SomeException(message, null) 会将 cause 初始化为 null,并且后续不能通过 initCause 来给 cause 赋值。但是 Lombok 注解生成的方法,可以通过 initCause 来赋值。

一般来说,调用 new SomeException(cause) ,也就是 super(cause),会使得 message 与 cause 相同。但是 Lombok 认为这样不对,cause 与 message 不应该被认为是相同,调用 Lombok 生成的 new SomeException(cause) 会使得 message 为 null。

配置

虽然实验室功能可以通过精准控制是否能够在项目使用 lombok.xxx.flagUsage = [warning | error],但是要想所有实验室功能都不能在项目中使用,可以采用如下总开关:

lombok.experimental.flagUsage = [warning | error]

默认:不配置。如果配置的话,Lombok 会将使用使用实验室功能标记为警告或错误。

转载请注明出处:码谱记录 » Lombok实验室功能