JavaRush /Blog Java /Random-VI /Nghỉ giải lao #230. Bản ghi trong Java là gì và chúng hoạ...

Nghỉ giải lao #230. Bản ghi trong Java là gì và chúng hoạt động như thế nào?

Xuất bản trong nhóm
Nguồn: JavaTechOnline Bài viết này cung cấp cái nhìn sâu sắc về khái niệm Bản ghi trong Java với các ví dụ, bao gồm cú pháp, cách tạo và cách sử dụng chúng. Nghỉ giải lao #230.  Bản ghi trong Java là gì và chúng hoạt động như thế nào - 1Bản ghi trong Java là một trong những tính năng tuyệt vời được giới thiệu lần đầu tiên trong Java 14 dưới dạng tính năng xem trước và cuối cùng xuất hiện trong bản phát hành Java 17. Nhiều nhà phát triển tích cực sử dụng nó, điều này giúp họ giảm thành công một lượng lớn mã soạn sẵn. Hơn nữa, nhờ có bản ghi, bạn không cần phải viết một dòng mã nào để biến một lớp thành bất biến.

Khi nào nên sử dụng Bản ghi trong Java?

Nếu bạn muốn truyền dữ liệu bất biến giữa các lớp khác nhau của ứng dụng thì sử dụng Record có thể là một lựa chọn tốt. Theo mặc định, Bản ghi trong Java là bất biến, có nghĩa là chúng ta không thể thay đổi thuộc tính của chúng sau khi chúng được tạo. Do đó, điều này giúp chúng tôi tránh được lỗi và cải thiện độ tin cậy của mã. Nói một cách đơn giản, bằng cách sử dụng Record trong Java, chúng ta có thể tự động tạo các lớp.

Tôi có thể sử dụng Bản ghi trong Java ở đâu?

Nói chung, chúng ta có thể sử dụng các bản ghi trong mọi trường hợp cần khai báo các thùng chứa dữ liệu đơn giản với các thuộc tính không thể thay đổi và các phương thức được tạo tự động. Ví dụ: bên dưới là một số trường hợp sử dụng mà bản ghi có thể hữu ích: Đối tượng truyền dữ liệu (DTO): Chúng ta có thể sử dụng Bản ghi để khai báo các đối tượng truyền dữ liệu đơn giản có chứa dữ liệu. Điều này rất hữu ích khi truyền dữ liệu giữa các lớp ứng dụng khác nhau, chẳng hạn như giữa lớp dịch vụ và lớp cơ sở dữ liệu. Đối tượng cấu hình : Bản ghi có thể được sử dụng để khai báo các đối tượng cấu hình có chứa một tập hợp các thuộc tính cấu hình cho một ứng dụng hoặc mô-đun. Những đối tượng này thường có các thuộc tính bất biến, làm cho chúng an toàn theo luồng và dễ sử dụng. Các đối tượng giá trị Bản ghi có thể được sử dụng để khai báo các đối tượng giá trị chứa một tập hợp các giá trị đại diện cho một khái niệm hoặc mô hình miền cụ thể. Phản hồi API : Khi tạo API REST, dữ liệu thường được trả về dưới dạng JSON hoặc XML. Trong những trường hợp như vậy, bạn có thể cần xác định cấu trúc dữ liệu đơn giản thể hiện phản hồi API. Các bản ghi rất lý tưởng cho việc này vì chúng cho phép bạn xác định cấu trúc dữ liệu nhẹ và không thay đổi, có thể dễ dàng được tuần tự hóa thành JSON hoặc XML. Dữ liệu thử nghiệm. Khi viết bài kiểm thử đơn vị, bạn thường cần tạo dữ liệu kiểm thử đại diện cho một kịch bản cụ thể. Trong những trường hợp như vậy, bạn có thể cần xác định cấu trúc dữ liệu đơn giản đại diện cho dữ liệu thử nghiệm. Các bản ghi có thể lý tưởng cho việc này vì chúng cho phép chúng ta xác định cấu trúc dữ liệu nhẹ và bất biến với mã soạn sẵn tối thiểu. Các đối tượng giống bộ dữ liệu : Các bản ghi có thể được sử dụng để khai báo các đối tượng giống bộ dữ liệu chứa một số giá trị liên quan cố định. Điều này có thể hữu ích khi trả về nhiều giá trị từ một phương thức hoặc khi làm việc với các tập hợp các giá trị liên quan.

Bản ghi trong Java là gì?

Bản ghi trong Java là một lớp được thiết kế để lưu trữ dữ liệu. Nó tương tự như lớp Java truyền thống nhưng nhẹ hơn và có một số tính năng độc đáo. Theo mặc định, các bản ghi là bất biến, có nghĩa là trạng thái của chúng không thể thay đổi sau khi được tạo. Điều này khiến chúng trở nên lý tưởng để lưu trữ dữ liệu bất biến, chẳng hạn như tham số cấu hình hoặc giá trị được trả về từ truy vấn cơ sở dữ liệu. Đó cũng là một cách để tạo kiểu dữ liệu tùy chỉnh bao gồm một tập hợp các trường hoặc biến, cũng như các phương thức truy cập và sửa đổi các trường đó. Bản ghi trong Java giúp làm việc với dữ liệu dễ dàng hơn bằng cách giảm số lượng mã soạn sẵn mà các nhà phát triển phải viết đi viết lại. Cú pháp để tạo một mục trong Java là:
record Record_Name(Fields....)

Làm cách nào để tạo và sử dụng Bản ghi trong Java bằng các ví dụ?

Hãy xem cách tạo và sử dụng bản ghi trong Java theo chương trình.

Tạo một bản ghi

Việc tạo một bản ghi theo chương trình không giống lắm với việc tạo một lớp thông thường trong Java. Thay vì class chúng ta sử dụng từ khóa record . Bạn cũng cần khai báo các trường có kiểu dữ liệu trong ngoặc đơn của tên bản ghi. Đây là một ví dụ mã minh họa cách tạo một mục trong Java:
public record Book(String name, double price) { }
Trong ví dụ này, chúng tôi đã tạo một bản ghi có tên Book với hai trường: nameprice . Từ khóa public chỉ ra rằng mục này có thể được truy cập bên ngoài gói mà nó được xác định.

Sử dụng bản ghi

Để sử dụng một bản ghi trong Java, chúng ta có thể khởi tạo nó bằng từ khóa new, giống như cách chúng ta làm với một lớp thông thường. Đây là một ví dụ:
Book book = new Book( "Core Java" , 324.25);
Ở đây, một phiên bản bản ghi Sách mới được tạo với trường tên được đặt thành Core Java và trường giá được đặt thành 324,25 . Khi một bản ghi được tạo, các trường của nó có thể được truy cập bằng tên của chính trường đó. Không có phương thức get hoặc set. Thay vào đó, tên trường sẽ trở thành tên phương thức.
String name = book.name();

double price = book.price();
Ở đây chúng ta thấy giá trị của trường têngiá được lấy từ bản ghi Sách . Ngoài các trường, bản ghi còn có một số phương thức tích hợp sẵn mà bạn có thể sử dụng để thao tác với chúng. Ví dụ: toString() , Equals()hashCode() . Hãy nhớ rằng các bản ghi Java theo mặc định là bất biến, có nghĩa là một khi chúng được tạo, trạng thái của chúng không thể thay đổi. Hãy xem một ví dụ khác về cách tạo và sử dụng bản ghi trong Java. Hãy lấy một trường hợp chúng ta cần một bản ghi để lưu trữ thông tin về một học sinh.
public record Student(String name, int age, String subject) {}
Điều này tạo ra một bản ghi có tên là Sinh viên với ba trường: tên , tuổichủ đề . Chúng ta có thể tạo một thể hiện của mục này như sau:
Student student = new Student("John Smith", 20, "Computer Science");
Sau đó chúng ta có thể lấy các giá trị trường bằng tên trường:
String name = student.name();
int age = student.age();
String subject= student.subject();

Bản ghi trông như thế nào sau khi biên dịch?

Record chỉ là một loại lớp đặc biệt nên trình biên dịch cũng chuyển nó thành một lớp thông thường, nhưng có một số hạn chế và khác biệt. Khi trình biên dịch chuyển đổi một bản ghi (tệp Java) thành mã byte sau quá trình biên dịch, tệp .class được tạo có chứa một số khai báo bổ sung của lớp Record . Ví dụ: bên dưới là mã byte được trình biên dịch Java tạo cho mục nhập Sinh viên :
record Student(String name, int age) {   }

Ghi vào tệp .class (sau khi biên dịch)

Chúng ta cũng có thể tìm thấy lớp được chuyển đổi bên dưới nếu chúng ta sử dụng công cụ javap và áp dụng lệnh bên dưới từ dòng lệnh. Điều quan trọng cần lưu ý là dòng lệnh Java bao gồm công cụ javap mà bạn có thể sử dụng để xem thông tin về các trường, hàm tạo và các phương thức của tệp lớp. >javap Sinh viên Kết luận: Nếu chúng tôi kiểm tra mã byte một cách cẩn thận, chúng tôi có thể có một số quan sát:
  • Trình biên dịch đã thay thế từ khóa Record bằng class .
  • Trình biên dịch đã khai báo lớp cuối cùng . Điều này chỉ ra rằng lớp này không thể được mở rộng. Điều này cũng có nghĩa là nó không thể được kế thừa và có bản chất bất biến.
  • Lớp được chuyển đổi mở rộng java.lang.Record . Điều này chỉ ra rằng tất cả các bản ghi là một lớp con của lớp Record được định nghĩa trong gói java.lang .
  • Trình biên dịch thêm một hàm tạo được tham số hóa.
  • Trình biên dịch tự động tạo ra các phương thức toString() , hashCode()Equals() .
  • Trình biên dịch đã thêm các phương thức để truy cập các trường. Hãy chú ý đến quy ước đặt tên phương thức - chúng hoàn toàn giống với tên trường, không được có get hoặc set trước tên trường .

Các trường trong bản ghi

Các bản ghi trong Java xác định trạng thái của chúng bằng cách sử dụng một tập hợp các trường, mỗi trường có tên và loại khác nhau. Các trường của bản ghi được khai báo trong tiêu đề bản ghi. Ví dụ:
public record Person(String name, int age) {}
Mục này có hai trường: tên loại Stringtuổi loại int . Các trường của bản ghi hoàn toàn là cuối cùng và không thể được gán lại sau khi bản ghi được tạo. Chúng tôi có thể thêm một trường mới, nhưng điều này không được khuyến khích. Trường mới được thêm vào bản ghi phải ở dạng tĩnh. Ví dụ:
public record Person(String name, int age) {

   static String sex;
}

Hàm tạo trong bản ghi

Giống như các hàm tạo lớp truyền thống, hàm tạo bản ghi được sử dụng để tạo các thể hiện bản ghi. Bản ghi có hai khái niệm về hàm tạo: hàm tạo chuẩn và hàm tạo nhỏ gọn . Hàm tạo nhỏ gọn cung cấp một cách ngắn gọn hơn để khởi tạo các biến trạng thái trong một bản ghi, trong khi hàm tạo chuẩn cung cấp một cách truyền thống hơn và linh hoạt hơn.

Hàm tạo chuẩn

Trình biên dịch Java mặc định cung cấp cho chúng ta một hàm tạo toàn đối số (hàm tạo toàn trường) để gán các đối số của nó cho các trường tương ứng. Nó được gọi là hàm tạo chuẩn. Chúng ta cũng có thể thêm logic nghiệp vụ như câu lệnh có điều kiện để xác thực dữ liệu. Dưới đây là một ví dụ:
public record Person(String name, int age) {

       public Person(String name, int age) {
           if (age < 18) {
              throw new IllegalArgumentException("You are not allowed to participate in general elections");
           }
      }
}

Nhà thiết kế nhỏ gọn

Hàm tạo nhỏ gọn bỏ qua tất cả các đối số, bao gồm cả dấu ngoặc đơn. Các trường tương ứng được gán tự động. Ví dụ: đoạn mã sau thể hiện khái niệm về hàm tạo nhỏ gọn trong Bản ghi :
public record Person(String name, int age) {

      public Person {
          if (age < 18) {
              throw new IllegalArgumentException("You are not allowed to participate in general elections");
          }
     }
}
Ngoài hàm tạo nhỏ gọn, bạn có thể định nghĩa các hàm tạo thông thường trong Record , giống như trong một lớp thông thường. Tuy nhiên, bạn cần đảm bảo rằng tất cả các hàm tạo đều khởi tạo tất cả các trường của bản ghi.

Phương pháp trong bản ghi

Các bản ghi trong Java tự động tạo một tập hợp các phương thức cho từng trường trong bản ghi. Các phương thức này được gọi là phương thức truy cập và có cùng tên với trường mà chúng được liên kết. Ví dụ: nếu một bản ghi có một trường có tên là price thì nó sẽ tự động có một phương thức gọi là price() trả về giá trị của trường price . Ngoài các phương thức truy cập được tạo tự động, chúng ta cũng có thể xác định các phương thức của riêng mình trong một mục nhập, giống như trong một lớp thông thường. Ví dụ:
public record Person(String name, int age) {
   public void sayHello() {
      System.out.println("Hello, my name is " + name);
   }
}
Mục này có phương thức sayHello() in ra lời chào bằng trường tên .

Cách Record giúp giảm mã soạn sẵn

Hãy xem một ví dụ đơn giản để biết các ký hiệu Java có thể giúp loại bỏ mã soạn sẵn như thế nào. Giả sử chúng ta có một lớp tên là Person đại diện cho một người có tên, tuổi và địa chỉ email. Đây là cách chúng ta định nghĩa một lớp theo cách truyền thống. Mã không sử dụng Record :
public class Person {

    private String name;
    private int age;
    private String email;

    public Person(String name, int age, String email) {
        this.name = name;
        this.age = age;
        this.email = email;
    }

    public String getName() {

       return name;
    }

    public int getAge() {
       return age;
    }

    publicString getEmail() {
       return email;
    }

    public void setName(String name) {
       this.name = name;
    }

    public voidsetAge(int age) {
       this.age = age;
    }

    public void setEmail(String email) {
       this.email = email;
    }

    @Override
    public String toString() {
       return "Person{" +
         "name='" + name + '\'' +
         ", age=" + age +
         ", email='" + email + '\'' +
      '}';
    }
}
Như chúng ta có thể thấy, lớp này yêu cầu rất nhiều mã soạn sẵn để xác định các trường, hàm tạo, getters, setters và phương thức toString() . Ngoài ra, giả sử chúng ta muốn làm cho lớp này trở nên bất biến. Để làm được điều này chúng ta sẽ phải thực hiện thêm một số bước như:
  • Khai báo lớp là lớp cuối cùng để nó không thể được mở rộng.
  • Khai báo tất cả các trường riêng tưcuối cùng để chúng không thể thay đổi bên ngoài hàm tạo.
  • Không cung cấp bất kỳ phương thức setter nào cho các trường.
  • Nếu bất kỳ trường nào có thể thay đổi, bạn nên trả về một bản sao của chúng thay vì trả về đối tượng ban đầu.
Mã sử ​​dụng Record :
public record Person(String name, int age, String email) {}
Đó là tất cả! Chỉ với một dòng mã, chúng ta đã định nghĩa một lớp có cùng các trường, hàm tạo, getters, setters và phương thức toString() giống như một lớp truyền thống. Cú pháp Record đảm nhiệm tất cả mã soạn sẵn cho chúng ta. Từ ví dụ trên, rõ ràng rằng việc sử dụng các bản ghi trong Java có thể giúp loại bỏ mã soạn sẵn bằng cách cung cấp cú pháp ngắn gọn hơn để xác định các lớp với một tập hợp các trường cố định. Cần ít mã hơn để xác định và sử dụng bản ghi so với phương pháp truyền thống và bản ghi cung cấp quyền truy cập trực tiếp vào các trường bằng cách sử dụng các phương pháp cập nhật trường. Điều này làm cho mã dễ đọc hơn, dễ bảo trì hơn và ít xảy ra lỗi hơn. Ngoài ra, với cú pháp Record , chúng ta không cần phải làm gì thêm để khiến lớp trở nên bất biến. Theo mặc định, tất cả các trường trong bản ghi là trường cuối cùng và bản thân lớp bản ghi là bất biến. Do đó, việc tạo một lớp bất biến bằng cú pháp ghi đơn giản hơn nhiều và yêu cầu ít mã hơn so với cách tiếp cận truyền thống. Với các bản ghi, chúng ta không cần khai báo các trường là cuối cùng , cung cấp hàm tạo để khởi tạo các trường hoặc cung cấp getters cho tất cả các trường.

Các lớp bản ghi chung

Java cũng có thể định nghĩa các lớp Bản ghi chung . Lớp đầu vào chung là lớp đầu vào có một hoặc nhiều tham số kiểu. Đây là một ví dụ:
public record Pair<T, U>(T first, U second) {}
Trong ví dụ này , Pair là một lớp bản ghi chung có hai tham số loại TU. Chúng ta có thể tạo một thể hiện của bản ghi này như sau:
Pair<String, Integer>pair = new Pair<>( "Hello" , 123);

Lớp lồng nhau bên trong Record

Bạn cũng có thể định nghĩa các lớp và giao diện lồng nhau trong một mục. Điều này hữu ích cho việc nhóm các lớp và giao diện có liên quan, đồng thời có thể giúp cải thiện tổ chức và khả năng bảo trì cơ sở mã của bạn. Đây là một ví dụ về một mục chứa một lớp lồng nhau:
public record Person(String name, int age, Contact contact){

    public static class Contact {

       private final String email;
       private final String phone;

       public Contact(String email, String phone){
           this.email = email;
           this.phone = phone;
       }

       public String getEmail(){
          return email;
       }

       public String getPhone(){
          return phone;
       }
    }
}
Trong ví dụ này, Person là một mục chứa lớp Liên hệ lồng nhau . Ngược lại, Liên hệ là một lớp lồng nhau tĩnh chứa hai trường riêng tư cuối cùng: địa chỉ email và số điện thoại. Nó cũng có một hàm tạo chấp nhận email và số điện thoại cũng như hai phương thức getter: getEmail()getPhone() . Chúng ta có thể tạo một cá thể Person như thế này:
Person person = new Person("John",30, new Person.Contact("john@example.com", "123-456-7890"));
Trong ví dụ này, chúng tôi đã tạo một đối tượng Person mới có tên John , 30 tuổi và một đối tượng Liên hệ mới có email john@example.com và số điện thoại 123-456-7890 .

Giao diện lồng nhau bên trong Record

Đây là một mục ví dụ chứa giao diện lồng nhau:
public record Book(String title, String author, Edition edition){
    public interface Edition{
       String getName();
   }
}
Trong ví dụ này, Book là mục chứa giao diện Edition lồng nhau . Đổi lại, Edition là một giao diện xác định một phương thức getName() duy nhất . Chúng ta có thể tạo một instance Book như sau:
Book book = new Book("The Hitchhiker's Guide to the Galaxy", "Douglas Adams", new Book.Edition() {

   public String getName() {

      return "Science Fiction";
   }
});
Trong ví dụ này, chúng tôi tạo một đối tượng Sách mới có tiêu đề Hướng dẫn về thiên hà của người đi nhờ xe của Douglas Adams và một cách triển khai ẩn danh mới của giao diện Edition sẽ trả về tên Khoa học viễn tưởng khi phương thức getName() được gọi .

Records có thể làm gì khác?

  • Các mục có thể xác định các hàm tạo tùy chỉnh. Các bản ghi hỗ trợ các hàm tạo được tham số hóa, có thể gọi một hàm tạo mặc định với các tham số được cung cấp trong phần nội dung của chúng. Ngoài ra, các bản ghi cũng hỗ trợ các hàm tạo nhỏ gọn, tương tự như các hàm tạo mặc định nhưng có thể bao gồm chức năng bổ sung như kiểm tra phần thân của hàm tạo.
  • Giống như bất kỳ lớp nào khác trong Java, Record có thể định nghĩa và sử dụng các phương thức cá thể. Điều này có nghĩa là chúng ta có thể tạo và gọi các phương thức cụ thể cho lớp ghi âm.
  • Trong Record , không được phép xác định các biến thể hiện là thành viên của lớp vì chúng chỉ có thể được chỉ định làm tham số hàm tạo. Tuy nhiên, bản ghi hỗ trợ các trường tĩnh và phương thức tĩnh, có thể được sử dụng để lưu trữ và truy cập dữ liệu chung cho tất cả các phiên bản của lớp bản ghi.

Bản ghi có thể triển khai giao diện không?

Có, viết bằng Java có thể triển khai các giao diện. Ví dụ: đoạn mã dưới đây thể hiện khái niệm:
public interface Printable {
   void print();
}
public record Person(String name, int age) implements Printable {
   public void print() {
      System.out.println("Name: " + name + ", Age: " + age);
   }
}
Ở đây chúng ta đã định nghĩa một giao diện Printable với một phương thức print() duy nhất . Chúng tôi cũng đã xác định một mục Person triển khai giao diện Printable . Bản ghi Person có hai trường: nameage , đồng thời ghi đè phương thức print của giao diện Printable để in các giá trị của các trường này. Chúng ta có thể khởi tạo một mục Person và gọi phương thức in của nó như sau:
Person person = new Person("John", 30);
person.print();
Điều này sẽ xuất Name: John, Age: 30 ra bàn điều khiển . Như trong ví dụ, các bản ghi Java có thể triển khai các giao diện giống như các lớp thông thường. Điều này có thể hữu ích khi thêm hành vi vào một mục hoặc để đảm bảo rằng mục nhập tuân thủ hợp đồng được xác định bởi giao diện.
Bình luận
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION