拨开荷叶行,寻梦已然成。仙女莲花里,翩翩白鹭情。
IMG-LOGO
主页 文章列表 Lombok使用@With注释

Lombok使用@With注释

白鹭 - 2022-03-07 2283 0 2

一、介绍

Lombok 是一个库,可帮助我们在编写Java 应用程序时显著减少样板代码。

在本教程中,我们将看到如何使用此库制作仅更改单个属性的不可变对象的副本。

2. 用法

当使用不允许设置器的不可变对象时,我们可能需要一个与当前对像类似的对象,但只有一个属性不同。这可以使用Lombok 的@With注释来实现:

public class User {
 private final String username;
 private final String emailAddress;
 @With
 private final boolean isAuthenticated;
 //getters, constructors
 }

上面的注释在后台生成以下内容:

public class User {
 private final String username;
 private final String emailAddress;
 private final boolean isAuthenticated;
 //getters, constructors
 public User withAuthenticated(boolean isAuthenticated) {
 return this.isAuthenticated == isAuthenticated ? this : new User(this.username, this.emailAddress, isAuthenticated);
 }
 }

然后我们可以使用上面生成的方法来创建原始对象的变异副本:

User immutableUser = new User("testuser", "test@1ju.org", false);
 User authenticatedUser = immutableUser.withAuthenticated(true);
 assertNotSame(immutableUser, authenticatedUser);
 assertFalse(immutableUser.isAuthenticated());
 assertTrue(authenticatedUser.isAuthenticated());

此外,我们可以选择注释整个类,这将为所有属性withX()方法

三、要求

@With注释,我们需要提供一个全参数构造函数从上面的例子我们可以看出,生成的方法需要this 来创建原始对象的克隆。

我们可以使用Lombok 自己的@AllArgsConstructor@Value注释来满足此要求。或者,我们也可以手动提供此构造函数,同时确保类中非静态属性的顺序与构造函数的顺序相匹配。

我们应该记住,如果在静态字段上使用@With**注释,则什么也不做。这是因为静态属性不被视为对象状态的一部分。此外,Lombok 会跳过$符号开头的字段**的方法生成。

4. 高级用法

让我们研究一下使用此注释时的一些高级场景。

4.1.抽像类

我们可以在抽像类的字段上@With

public abstract class Device {
 private final String serial;
 @With
 private final boolean isInspected;
 //getters, constructor
 }

但是,我们需要为生成的withInspected()方法提供一个实现。这是因为Lombok 不知道我们抽像类的具体实现来创建它的克隆:

public class KioskDevice extends Device {
 @Override
 public Device withInspected(boolean isInspected) {
 return new KioskDevice(getSerial(), isInspected);
 }
 //getters, constructor
 }

4.2.命名约定

如上所述,Lombok 将跳过以$符号开头的字段。但是,如果字段以字符开头,则它是标题大小写的,最后,with是生成方法的前缀。

或者,如果该字段以下划线开头,则with只是作为生成方法的前缀:

public class Holder {
 @With
 private String variableA;
 @With
 private String _variableB;
 @With
 private String $variableC;
 //getters, constructor excluding $variableC
 }

根据上面的代码,我们看到只有前两个变量将为它们生成withX()

Holder value = new Holder("a", "b");
 Holder valueModifiedA = value.withVariableA("mod-a");
 Holder valueModifiedB = value.with_variableB("mod-b");
 // Holder valueModifiedC = value.with$VariableC("mod-c"); not possible

4.3.方法生成的例外

我们应该注意,除了以$符号开头的字段之外,如果我们的类中已经存在withX()方法,Lombok 将不会生成它:

public class Stock {
 @With
 private String sku;
 @With
 private int stockCount;
 //prevents another withSku() method from being generated
 public Stock withSku(String sku) {
 return new Stock("mod-" + sku, stockCount);
 }
 //constructor
 }

在上述场景中,不会生成新的withSku()方法。

此外,Lombok在以下场景中会****跳过方法生成:

public class Stock {
 @With
 private String sku;
 private int stockCount;
 //also prevents another withSku() method from being generated
 public Stock withSKU(String... sku) {
 return sku == null || sku.length == 0 ?
 new Stock("unknown", stockCount) :
 new Stock("mod-" + sku[0], stockCount);
 }
 //constructor
 }

我们可以注意到上面withSKU()

基本上,如果出现以下情况,Lombok 将跳过方法生成:

  • 与生成的方法名存在相同的方法(忽略大小写)

  • 现有方法与生成的方法具有相同数量的参数(包括var-args)

4.4.生成方法的空验证

与其他Lombok 注释类似,我们可以@With注释生成的方法进行null

@With
 @AllArgsConstructor
 public class ImprovedUser {
 @NonNull
 private final String username;
 @NonNull
 private final String emailAddress;
 }

Lombok 将为我们生成以下代码以及所需的null检查:

public ImprovedUser withUsername(@NonNull String username) {
 if (username == null) {
 throw new NullPointerException("username is marked non-null but is null");
 } else {
 return this.username == username ? this : new ImprovedUser(username, this.emailAddress);
 }
 }
 public ImprovedUser withEmailAddress(@NonNull String emailAddress) {
 if (emailAddress == null) {
 throw new NullPointerException("emailAddress is marked non-null but is null");
 } else {
 return this.emailAddress == emailAddress ? this : new ImprovedUser(this.username, emailAddress);
 }
 }

5. 结论

在本文中,我们已经看到了如何使用Lombok 的@With注释来生成具有单个字段更改的特定对象的克隆。

我们还了解了此方法生成的实际工作方式和时间,以及如何通过其他验证(例如null检查)来增强它。


标签:

0 评论

发表评论

您的电子邮件地址不会被公开。 必填的字段已做标记 *