JavaRush /Java Blog /Random-TW /喝咖啡休息#128。Java 記錄指南

喝咖啡休息#128。Java 記錄指南

在 Random-TW 群組發布
來源:abhinavpandey.dev 在本教學中,我們將介紹在 Java 中使用 Records 的基礎知識。Java 14 中引入了記錄,作為一種在利用不可變物件的同時刪除建立 Value 物件的樣板程式碼的方法。 喝咖啡休息#128。 Java 記錄指南 - 1

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. 值物件的不變性

值物件必須是不可變的。這意味著我們必須限制更改物件欄位的方式。出於以下原因,建議這樣做:
  • 避免意外更改欄位值的風險。
  • 確保平等的物體終生保持不變。
由於Contact類別已經是不可變的,我們現在:
  1. 使這些領域成為私有的最終的
  2. 只為每個字段提供一個 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. 預設行為

我們已將程式碼減少到一行。讓我們列出它所包含的內容:
  1. 預設情況下,姓名電子郵件欄位是私人且最終的。

  2. 該程式碼定義了一個將欄位作為參數的「規範建構函數」。

  3. 可以透過類似於 getter 的方法存取欄位 - name()email()。欄位沒有設定器,因此物件中的資料變得不可變。

  4. 實作了toString方法來列印字段,就像我們為Contact類別所做的那樣。

  5. 實作了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 +
                ']';
    }
}
同樣,我們可以重寫equalshashCode方法。

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;
    }
}
請注意,靜態欄位中沒有隱式限制。如有必要,它們可能會公開,但不是最終版本。

結論

記錄是定義資料類別的好方法。它們比 JavaBeans/POJO 方法更方便、更強大。因為它們易於實現,所以它們應該比其他創建值物件的方法更受青睞。
留言
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION