來源:abhinavpandey.dev 在本教學中,我們將介紹在 Java 中使用 Records 的基礎知識。Java 14 中引入了記錄,作為一種在利用不可變物件的同時刪除建立 Value 物件的樣板程式碼的方法。
1. 基本概念
在我們深入研究條目本身之前,讓我們先看看它們解決的問題。為此,我們必須記住 Java 14 之前如何建立值物件。1.1. 值對象
值物件是 Java 應用程式的一個組成部分。它們儲存需要在應用程式層之間傳輸的資料。值物件包含欄位、建構函式以及用於存取這些欄位的方法。下面是一個值物件的範例:public class Contact {
private final String name;
private final String email;
public Contact(String name, String email) {
this.name = name;
this.email = email;
}
public String getName() {
return name;
}
public String getEmail() {
return email;
}
}
1.2. 值物件之間的相等性
值物件也可以提供一種比較它們是否相等的方法。預設情況下,Java透過比較物件的記憶體位址來比較物件的相等性。然而,在某些情況下,包含相同資料的物件可能被認為是相等的。為了實現這一點,我們可以重寫equals和.hashCode方法。讓我們為Contact類別實作它們:public class Contact {
// ...
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Contact contact = (Contact) o;
return Object.equals(email, contact.email) &&
Objects.equals(name, contact.name);
}
@Override
public int hashCode() {
return Objects.hash(name, email);
}
}
1.3. 值物件的不變性
值物件必須是不可變的。這意味著我們必須限制更改物件欄位的方式。出於以下原因,建議這樣做:- 避免意外更改欄位值的風險。
- 確保平等的物體終生保持不變。
- 使這些領域成為私有的和最終的。
- 只為每個字段提供一個 getter(沒有setters)。
1.4. 註冊值對象
很多時候我們需要註冊物件中包含的值。這是透過提供toString方法來完成的。每當註冊或列印物件時,都會呼叫toString方法。這裡最簡單的方法是列印每個欄位的值。這是一個例子:public class Contact {
// ...
@Override
public String toString() {
return "Contact[" +
"name='" + name + '\'' +
", email=" + email +
']';
}
}
2. 使用記錄減少模板
由於大多數值物件具有相同的需求和功能,因此簡化創建它們的過程會很好。讓我們看看錄音如何幫助實現這一目標。2.1. 將 Person 類別轉換為 Record
讓我們建立一個與上面定義的Contact類別具有相同功能的Contact類別條目。public record Contact(String name, String email) {}
record 關鍵字用於建立Record類別。呼叫者可以按照與類別相同的方式處理記錄。例如,要建立一個新的條目實例,我們可以使用new關鍵字。
Contact contact = new Contact("John Doe", "johnrocks@gmail.com");
2.2. 預設行為
我們已將程式碼減少到一行。讓我們列出它所包含的內容:-
預設情況下,姓名和電子郵件欄位是私人且最終的。
-
該程式碼定義了一個將欄位作為參數的「規範建構函數」。
-
可以透過類似於 getter 的方法存取欄位 - name()和email()。欄位沒有設定器,因此物件中的資料變得不可變。
-
實作了toString方法來列印字段,就像我們為Contact類別所做的那樣。
-
實作了equals和.hashCode方法。它們包括所有字段,就像Contact類別一樣。
2.3 規範構造函數
預設建構函數將所有欄位作為輸入參數並將它們設為欄位。例如,預設的規範建構函數如下所示:public Contact(String name, String email) {
this.name = name;
this.email = email;
}
如果我們在記錄類別中定義具有相同簽名的建構函數,則將使用它而不是規範建構函數。
3. 處理記錄
我們可以透過多種方式改變條目的行為。讓我們來看看一些用例以及如何實現它們。3.1. 覆蓋預設實現
任何預設實作都可以透過覆蓋它來更改。例如,如果我們想要更改toString方法的行為,那麼我們可以在大括號{}之間覆寫它。public record Contact(String name, String email) {
@Override
public String toString() {
return "Contact[" +
"name is '" + name + '\'' +
", email is" + email +
']';
}
}
同樣,我們可以重寫equals和hashCode方法。
3.2. 緊湊型施工套件
有時我們希望建構函式做的不只是初始化欄位。為此,我們可以將必要的操作添加到緊湊構造函數中的條目中。之所以稱為緊湊,是因為它不需要定義欄位初始化或參數列表。public record Contact(String name, String email) {
public Contact {
if(!email.contains("@")) {
throw new IllegalArgumentException("Invalid email");
}
}
}
請注意,沒有參數列表,並且在執行檢查之前,名稱和電子郵件的初始化在後台進行。
3.3. 新增構造函數
您可以為一筆記錄新增多個建構函式。讓我們來看幾個範例和限制。首先,讓我們加入新的有效建構函數:public record Contact(String name, String email) {
public Contact(String email) {
this("John Doe", email);
}
// replaces the default constructor
public Contact(String name, String email) {
this.name = name;
this.email = email;
}
}
在第一種情況下,使用this關鍵字 存取預設建構函數。第二個建構函數會覆寫預設建構函數,因為它具有相同的參數列表。在這種情況下,條目本身不會建立預設建構函式。對構造函數有一些限制。
1. 應始終從任何其他構造函數呼叫預設構造函數。
例如,下面的程式碼將無法編譯:public record Contact(String name, String email) {
public Contact(String name) {
this.name = "John Doe";
this.email = null;
}
}
此規則確保欄位始終被初始化。也保證緊湊構造函數中定義的操作始終被執行。
2. 如果定義了緊湊構造函數,則無法覆寫預設構造函數。
定義緊湊構造函數時,會自動建立一個具有初始化和緊湊構造函數邏輯的預設構造函數。在這種情況下,編譯器將不允許我們定義與預設建構函式具有相同參數的建構函式。例如,在這段程式碼中,編譯不會發生:public record Contact(String name, String email) {
public Contact {
if(!email.contains("@")) {
throw new IllegalArgumentException("Invalid email");
}
}
public Contact(String name, String email) {
this.name = name;
this.email = email;
}
}
3.4. 實現介面
與任何類別一樣,我們可以在記錄中實作介面。public record Contact(String name, String email) implements Comparable<Contact> {
@Override
public int compareTo(Contact o) {
return name.compareTo(o.name);
}
}
重要的提示。為了確保完全的不變性,記錄不能被繼承。參賽作品是最終作品,不能擴大。他們也不能擴展其他課程。
3.5. 添加方法
除了重寫方法和介面實作的建構函數之外,我們還可以添加任何我們想要的方法。例如:public record Contact(String name, String email) {
String printName() {
return "My name is:" + this.name;
}
}
我們也可以加入靜態方法。例如,如果我們想要一個靜態方法傳回一個正規表示式,我們可以根據該正規表示式檢查電子郵件,那麼我們可以如下所示定義它:
public record Contact(String name, String email) {
static Pattern emailRegex() {
return Pattern.compile("^[A-Z0-9._%+-]+@[A-Z0-9.-]+\\.[A-Z]{2,6}$", Pattern.CASE_INSENSITIVE);
}
}
3.6. 新增字段
我們無法將實例欄位新增至記錄。但是,我們可以新增靜態字段。public record Contact(String name, String email) {
private static final Pattern EMAIL_REGEX_PATTERN = Pattern
.compile("^[A-Z0-9._%+-]+@[A-Z0-9.-]+\\.[A-Z]{2,6}$", Pattern.CASE_INSENSITIVE);
static Pattern emailRegex() {
return EMAIL_REGEX_PATTERN;
}
}
請注意,靜態欄位中沒有隱式限制。如有必要,它們可能會公開,但不是最終版本。
GO TO FULL VERSION