Lombok 的实验性功能不需要额外的安装即可使用,不过,它们可能得不到更好的支持:
- 没有完善的测试,不如核心核心功能那样稳定
- 修复 bug 的速度没有核心功能快
- 如果后面有更好的实现方案,API 可能有较大变化
- 如果该实验性功能比较难支持或者没有足够的使用,未来也可能移除掉
当然,如果某个实验室功能社区反映良好,接受度较高的话,将会从实验室中转移到核心包中。
@Accessors
提供更灵活的getter和setter方法生成,允许自定义命名规则,如生成fluent风格的访问器。
🔬 实验室
该特性目前属于实验性功能主要基于以下几点考虑:
- 希望 @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 设置。
@ExtensionMethod
允许公共静态方法表现得像实例方法。
🔬 实验室
该特性目前属于实验性功能主要基于以下几点考虑:
- 对代码风格影响很大。
- 真的很想提供实用方法来扩展公共类,但到目前为止,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 注解是添加在需要使用静态方法的类上的,而不是添加到实现静态方法的类上。
@FieldDefaults
为类或枚举的每个字段提供默认访问修饰符,如private或final。
🔬 实验室
该特性目前属于实验性功能主要基于以下几点考虑:
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。
@Delegate
允许一个类委托其方法给另一个类或字段。
🔬 实验室
该特性目前属于实验性功能主要基于以下几点考虑:
- 使用的不多;
- 难以支持边缘情况,例如递归委托;
- 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) 。
onMethod/onConstructor/onParam
给注解添加注解:用在 Lombok 注解的参数中,用于给Lombok生成的方法添加注解。为了简化它们 ,下面用 onX 代替 onMethod/onConstructor/onParam 。
🔬 实验室
该特性目前属于实验性功能主要基于以下几点考虑:
- 语法看起来不太美观。
- 可能 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 之后添加下划线。
@UtilityClass
自动将类转换为工具类,所有成员都是静态的。
🔬 实验室
该特性目前属于实验性功能主要基于以下几点考虑:
- 关于它是否足够普遍,可以算作样板,存在一些争论。
- 由于 Javac 的限制,静态导入可能存在问题。
📋 概述
工具类包含一系列实用方法:它不需要创建实例对象,所有成员都是静态的。常见的工具类有 java.lang.Math
和java.util.Collections
等。该注解就是自动将一个类转换为工具类。
工具类不能被实例化:使用 @UtilityClass 注解后,该类中的构造函数会被私有化,类会被标记为 final。如果该类为内部类,会被标记为 static。类中所有成员都会被标记为 static ,包括字段和内部类。
@Helper
允许在方法中声明并使用类,提供类似lambda表达式的功能。
🔬 实验室
该特性目前属于实验性功能主要基于以下几点考虑:
- 使用 lambda 表达式可以实现相同的效果。
- 提供的模板能力较少,对开发的帮助有限。
📋 概述
此注释允许你将方法放入方法中。如果想将方法声明在方法中,需要在方法中先声明一个 class,在该 class 中定义的方法可以访问在此 class 之前声明的局部变量或参数。不过要想调用该 class 中的方法,需要先实例化该 class。使用了 @Helper 注解后,可以不用实例化 class 了,该 class 中的方法可以直接调用。
要实例化方法中定义的 class,需要使用到 HelperClass h = new HelperClass();
,然后使用 h.helperMethod();
调用此 class 中的方法。有了 @Helper 注解后,可以直接方法 class 中定义的方法 helperMethod();
。
@FieldNameConstants
可以让该类中的字段拥有一个与字段名相同的字符串常量。
🔬 实验室
该特性目前属于实验性功能主要基于以下几点考虑:
- 不确定是否对代码有破坏性。
- 作为刚刚推出的功能,仍在收集反馈。
📋 概述
@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
来精确控制需要处理的字段。
@SuperBuilder
为类生成复杂的构建器API,与@Builder不同,它也适用于父类字段。该功能不兼容@Builder。
📋 概述
@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 “隐藏”该方法。例如有一个名为 date 的字段被添加了 @Setter 注解,此时 Lombok 应该生成一个 setDate()
方法,但是如果在类中存在一个 setDate()
方法,则不会生成该方法。 要是在该 setDate()
方法上添加了 @Tolerate 注解,则会生成一个 setDateXX() 方法。
@Jacksonized
作为@SuperBuilder和@Builder的附加注解,简化Jackson反序列化配置。
📋 概述
注解 @Jacksonized 是 @SuperBuilder 和 @Builder 的附加注释。它会自动将生成的构建器类配置为供 Jackson 的反序列化使用。它只有在存在 @Builder 或 @SuperBuilder 注解时才有效果;否则会发出警告。
如果没有 @Jacksonized ,则必须自定义构建器类。
🔔 说明
如果类名为 Foobar,@SuperBuilder 注解生成的类后缀为 Impl,则使用 @Jacksonized 注解后,会在生成的类上添加 Jackson 注解 @JsonDeserialize(builder=Foobar.FoobarBuilder[Impl].class))
,要是该注解已经存在了,则会报错。
将于Jackson 相关的注解复制到生成的 builder 类上。
如果使用了 Lombok 参数 setterPrefix,则其值会被添加到注解 @JsonPOJOBuilder(withPrefix="")
中。
@StandardException
简化自定义异常的构造函数生成。
📋 概述
在自定义异常上使用 @StandardException,可以帮助你生成如下 4 个构造函数:
- 无参构造函数
MyException()
- 仅包含 message 的构造函数
MyException(String message)
- 仅包含 cause 的构造函数
MyException(Throwable cause)
- 完整构造函数,包含 message 和 cause
MyException(String message, Throwable cause)
如果类中已经存在相同声明的构造函数,则不会生成该函数。
🔔 说明
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 会将使用使用实验室功能标记为警告或错误。