Lombok使用@With註釋
- java
一、介紹
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", "[email protected]", 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
檢查)來增強它。