JavaRush /Blog Java /Random-VI /Nghỉ giải lao #128. Hướng dẫn về bản ghi Java

Nghỉ giải lao #128. Hướng dẫn về bản ghi Java

Xuất bản trong nhóm
Nguồn: abhinavpandey.dev Trong hướng dẫn này, chúng tôi sẽ đề cập đến những kiến ​​thức cơ bản về cách sử dụng Bản ghi trong Java. Các bản ghi được giới thiệu trong Java 14 như một cách để loại bỏ mã soạn sẵn xung quanh việc tạo các đối tượng Giá trị trong khi tận dụng các đối tượng bất biến. Nghỉ giải lao #128.  Hướng dẫn ghi Java - 1

1. Các khái niệm cơ bản

Trước khi đi vào phần chính, chúng ta hãy xem xét vấn đề mà chúng giải quyết. Để làm điều này, chúng ta sẽ phải nhớ cách tạo các đối tượng giá trị trước Java 14.

1.1. Đối tượng giá trị

Các đối tượng giá trị là một phần không thể thiếu của các ứng dụng Java. Chúng lưu trữ dữ liệu cần được chuyển giữa các lớp ứng dụng. Một đối tượng giá trị chứa các trường, hàm tạo và phương thức để truy cập các trường đó. Dưới đây là một ví dụ về một đối tượng giá trị:
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. Sự bình đẳng giữa các đối tượng Giá trị

Các đối tượng giá trị cũng có thể cung cấp một cách để so sánh chúng về sự bình đẳng. Theo mặc định, Java so sánh sự bằng nhau của các đối tượng bằng cách so sánh địa chỉ bộ nhớ của chúng. Tuy nhiên, trong một số trường hợp, các đối tượng chứa cùng dữ liệu có thể được coi là bằng nhau. Để thực hiện điều này, chúng ta có thể ghi đè các phương thức bằng.hashCode . Hãy triển khai chúng cho lớp Liên hệ :
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. Tính bất biến của các đối tượng Giá trị

Các đối tượng giá trị phải bất biến. Điều này có nghĩa là chúng ta phải hạn chế những cách chúng ta có thể thay đổi các trường của một đối tượng. Điều này được khuyến khích vì những lý do sau:
  • Để tránh nguy cơ vô tình thay đổi giá trị trường.
  • Để đảm bảo rằng các đối tượng bình đẳng vẫn giống nhau trong suốt cuộc đời của chúng.
Vì lớp Liên hệ đã không thể thay đổi nên bây giờ chúng tôi:
  1. làm cho các trường ở chế độ riêng tưcuối cùng .
  2. chỉ cung cấp một getter cho mỗi trường (không có setters ).

1.4. Đăng ký đối tượng Giá trị

Thông thường chúng ta cần đăng ký các giá trị có trong đối tượng. Điều này được thực hiện bằng cách cung cấp phương thức toString . Bất cứ khi nào một đối tượng được đăng ký hoặc in ra, phương thức toString sẽ được gọi . Cách dễ nhất ở đây là in giá trị của từng trường. Đây là một ví dụ:
public class Contact {
    // ...
    @Override
    public String toString() {
        return "Contact[" +
                "name='" + name + '\'' +
                ", email=" + email +
                ']';
    }
}

2. Giảm mẫu bằng Bản ghi

Vì hầu hết các đối tượng giá trị đều có cùng nhu cầu và chức năng, nên sẽ rất tốt nếu bạn đơn giản hóa quá trình tạo chúng. Hãy xem cách ghi âm giúp đạt được điều này.

2.1. Chuyển đổi lớp Person thành Record

Hãy tạo một mục nhập lớp Liên hệ có chức năng tương tự như lớp Liên hệ được xác định ở trên.
public record Contact(String name, String email) {}
Từ khóa record được sử dụng để tạo ra một lớp Record . Bản ghi có thể được xử lý bởi người gọi giống như cách xử lý một lớp. Ví dụ: để tạo một thể hiện mục nhập mới, chúng ta có thể sử dụng từ khóa mới .
Contact contact = new Contact("John Doe", "johnrocks@gmail.com");

2.2. Hành vi mặc định

Chúng tôi đã giảm mã xuống còn một dòng. Hãy liệt kê những gì nó bao gồm:
  1. Các trường tênemail là riêng tư và cuối cùng theo mặc định.

  2. Mã này định nghĩa một "hàm tạo chuẩn" lấy các trường làm tham số.

  3. Các trường có thể truy cập được thông qua các phương thức giống getter - name()email() . Không có trình thiết lập cho các trường nên dữ liệu trong đối tượng trở nên bất biến.

  4. Đã triển khai phương thức toString để in các trường giống như chúng ta đã làm với lớp Liên hệ .

  5. Đã triển khai các phương thức bằng.hashCode . Chúng bao gồm tất cả các trường, giống như lớp Liên hệ .

2.3 Hàm tạo chuẩn

Hàm tạo mặc định lấy tất cả các trường làm tham số đầu vào và đặt chúng thành các trường. Ví dụ: Canonical Constructor mặc định được hiển thị bên dưới:
public Contact(String name, String email) {
    this.name = name;
    this.email = email;
}
Nếu chúng ta định nghĩa một hàm tạo có cùng chữ ký trong lớp ghi, nó sẽ được sử dụng thay cho hàm tạo chuẩn.

3. Làm việc với hồ sơ

Chúng ta có thể thay đổi hành vi của mục này theo nhiều cách. Hãy xem xét một số trường hợp sử dụng và cách đạt được chúng.

3.1. Ghi đè triển khai mặc định

Bất kỳ triển khai mặc định nào cũng có thể được thay đổi bằng cách ghi đè nó. Ví dụ: nếu muốn thay đổi hành vi của phương thức toString thì chúng ta có thể ghi đè nó giữa các dấu ngoặc nhọn {} .
public record Contact(String name, String email) {
    @Override
    public String toString() {
        return "Contact[" +
                "name is '" + name + '\'' +
                ", email is" + email +
                ']';
    }
}
Tương tự, chúng ta có thể ghi đè các phương thức bằnghashCode .

3.2. Bộ dụng cụ xây dựng nhỏ gọn

Đôi khi chúng ta muốn các nhà xây dựng thực hiện nhiều việc hơn là chỉ khởi tạo các trường. Để thực hiện điều này, chúng ta có thể thêm các thao tác cần thiết vào mục nhập của mình trong Compact Constructor. Nó được gọi là nhỏ gọn vì nó không cần xác định khởi tạo trường hoặc danh sách tham số.
public record Contact(String name, String email) {
    public Contact {
        if(!email.contains("@")) {
            throw new IllegalArgumentException("Invalid email");
        }
    }
}
Lưu ý rằng không có danh sách tham số và việc khởi tạo tênemail sẽ diễn ra ở chế độ nền trước khi thực hiện kiểm tra.

3.3. Thêm hàm tạo

Bạn có thể thêm nhiều hàm tạo vào một bản ghi. Hãy xem xét một vài ví dụ và hạn chế. Đầu tiên, hãy thêm các hàm tạo hợp lệ mới:
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;
    }
}
Trong trường hợp đầu tiên, hàm tạo mặc định được truy cập bằng từ khóa this . Hàm tạo thứ hai ghi đè hàm tạo mặc định vì nó có cùng danh sách tham số. Trong trường hợp này, bản thân mục nhập sẽ không tạo ra hàm tạo mặc định. Có một số hạn chế đối với các nhà xây dựng.

1. Hàm tạo mặc định phải luôn được gọi từ bất kỳ hàm tạo nào khác.

Ví dụ: mã bên dưới sẽ không biên dịch:
public record Contact(String name, String email) {
    public Contact(String name) {
        this.name = "John Doe";
        this.email = null;
    }
}
Quy tắc này đảm bảo rằng các trường luôn được khởi tạo. Nó cũng đảm bảo rằng các hoạt động được xác định trong hàm tạo nhỏ gọn luôn được thực thi.

2. Không thể ghi đè hàm tạo mặc định nếu hàm tạo nhỏ gọn được xác định.

Khi một hàm tạo nhỏ gọn được xác định, một hàm tạo mặc định sẽ tự động được tạo bằng logic khởi tạo và hàm tạo nhỏ gọn. Trong trường hợp này, trình biên dịch sẽ không cho phép chúng ta định nghĩa một hàm tạo có cùng đối số với hàm tạo mặc định. Ví dụ: trong mã này quá trình biên dịch sẽ không xảy ra:
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. Giao diện triển khai

Như với bất kỳ lớp nào, chúng ta có thể triển khai các giao diện trong các bản ghi.
public record Contact(String name, String email) implements Comparable<Contact> {
    @Override
    public int compareTo(Contact o) {
        return name.compareTo(o.name);
    }
}
Lưu ý quan trọng. Để đảm bảo tính bất biến hoàn toàn, các bản ghi không thể được kế thừa. Các mục nhập là cuối cùng và không thể mở rộng. Họ cũng không thể mở rộng các lớp khác.

3.5. Thêm phương pháp

Ngoài các hàm tạo ghi đè các phương thức và triển khai giao diện, chúng ta cũng có thể thêm bất kỳ phương thức nào chúng ta muốn. Ví dụ:
public record Contact(String name, String email) {
    String printName() {
        return "My name is:" + this.name;
    }
}
Chúng ta cũng có thể thêm các phương thức tĩnh. Ví dụ: nếu chúng ta muốn có một phương thức tĩnh trả về một biểu thức chính quy để chúng ta có thể kiểm tra email, thì chúng ta có thể định nghĩa nó như hiển thị bên dưới:
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. Thêm trường

Chúng tôi không thể thêm trường mẫu vào bản ghi. Tuy nhiên, chúng ta có thể thêm các trường tĩnh.
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;
    }
}
Lưu ý rằng không có hạn chế ngầm định nào trong các trường tĩnh. Nếu cần thiết, chúng có thể được công khai và không phải là cuối cùng.

Phần kết luận

Bản ghi là một cách tuyệt vời để xác định các lớp dữ liệu. Chúng thuận tiện và mạnh mẽ hơn nhiều so với cách tiếp cận JavaBeans/POJO. Bởi vì chúng dễ thực hiện nên chúng nên được ưu tiên hơn các cách tạo đối tượng giá trị khác.
Bình luận
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION