Bản ghi (Java 16+)
Thuộc phần Lập trình hướng đối tượng trong hành trình Java của Coddy. Bài học 51 trên 87.
Khi tạo các lớp đơn giản dùng để chứa dữ liệu, bạn thường phải viết nhiều đoạn code lặp lại: các trường private, hàm khởi tạo, getter, toString(), equals() và hashCode(). Java 16 giới thiệu record để loại bỏ công việc tẻ nhạt này.
record là một lớp đặc biệt dùng để lưu trữ dữ liệu bất biến. Bạn khai báo lớp này bằng từ khóa record, theo sau là danh sách thành phần:
record Person(String name, int age) { }
// Chỉ một dòng đó tự động tạo ra:
// - các trường private final: name và age
// - một hàm khởi tạo (constructor): Person(String name, int age)
// - các phương thức truy cập (accessor methods): name() và age()
// - toString(), equals() và hashCode()Cách dùng bản ghi rất đơn giản:
Person p = new Person("Alice", 30);
System.out.println(p.name()); // Alice
System.out.println(p.age()); // 30
System.out.println(p); // Person[name=Alice, age=30]Lưu ý rằng các phương thức truy cập không dùng tiền tố get: chúng chỉ được đặt tên theo các trường. Record mặc định là final, vì vậy không thể mở rộng chúng. Tất cả các trường cũng đều là final, khiến record vốn bất biến.
Bạn có thể thêm các phương thức tùy chỉnh và ghi đè các phương thức được tạo tự động nếu cần. Bạn cũng có thể thêm bước xác thực trong hàm khởi tạo rút gọn:
record Person(String name, int age) {
public Person {
if (age < 0) throw new IllegalArgumentException("Age cannot be negative");
}
}Bản ghi rất phù hợp với DTO, phản hồi API hoặc mọi trường hợp bạn cần một vùng chứa dữ liệu đơn giản, bất biến mà không phải viết code rườm rà.
Thử thách
DễHãy cùng xây dựng hệ thống đặt vé xem phim để khám phá sự tinh gọn của record trong Java. Bạn sẽ thấy record loại bỏ code rườm rà mà vẫn cho phép bạn thêm phương thức tùy chỉnh và bước xác thực.
Bạn sẽ sắp xếp code trong ba tệp:
Movie.java: Tạo một record đại diện cho một bộ phim với ba thành phần:title(String),genre(String) vàdurationMinutes(int). Thêm một phương thức tùy chỉnh có têngetFormattedDuration()để trả về thời lượng theo định dạng dễ đọc:[hours]h [minutes]m(ví dụ: 142 phút sẽ thành2h 22m). Dùng phép chia lấy phần nguyên và phép chia lấy dư để tính số giờ và số phút còn lại.Ticket.java: Tạo một record đại diện cho vé xem phim với ba thành phần:movie(Movie),seatNumber(String) vàprice(double). Thêm một hàm khởi tạo rút gọn để xác thực giá vé. Nếu giá nhỏ hơn hoặc bằng 0, hãy ném ngoại lệIllegalArgumentExceptionvới thông báoPrice must be positive. Đồng thời, thêm một phương thức tùy chỉnh có tênprintTicket()để trả về chuỗi nhiều dòng:========== TICKET ========== Movie: [movie title] Genre: [movie genre] Duration: [formatted duration from movie] Seat: [seatNumber] Price: $[price] ============================Main.java: Hoàn thiện hệ thống đặt vé! Bạn sẽ nhận năm đầu vào: tên phim (String), thể loại (String), thời lượng tính bằng phút (int), số ghế (String) và giá vé (double).Tạo một record Movie với ba đầu vào đầu tiên, sau đó tạo một record Ticket với phim, số ghế và giá vé. In ra kết quả khi gọi
printTicket()trên vé.
Bạn sẽ nhận năm đầu vào theo thứ tự: tên phim, thể loại, thời lượng (phút), số ghế và giá vé.
Hãy để ý record gọn gàng hơn lớp truyền thống đến mức nào: bạn không cần viết hàm khởi tạo, getter, toString(), equals() hay hashCode(). Hàm khởi tạo rút gọn cho phép bạn thêm bước xác thực mà không cần lặp lại các phép gán trường, và bạn vẫn có thể thêm phương thức tùy chỉnh khi cần!
Tự làm thử
import java.util.Scanner;
class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
// Đọc các giá trị đầu vào
String title = scanner.nextLine();
String genre = scanner.nextLine();
int durationMinutes = Integer.parseInt(scanner.nextLine());
String seatNumber = scanner.nextLine();
double price = Double.parseDouble(scanner.nextLine());
// TODO: Tạo bản ghi Movie với title, genre và durationMinutes
// TODO: Tạo bản ghi Ticket với phim, seatNumber và price
// TODO: In vé bằng phương thức printTicket()
}
}
Bài học này có một bài trắc nghiệm ngắn. Bắt đầu bài học để trả lời và theo dõi tiến độ.
Tất cả bài học trong Lập trình hướng đối tượng
1Kiến thức cơ bản về lập trình hướng đối tượng
Tệp bên ngoàiGiới thiệu về lập trình hướng đối tượngLớp và đối tượngTừ khóa thisPhương thứcTrường (thuộc tính)Phương thức khởi tạoNạp chồng phương thức khởi tạoÔn tập: Máy tính đơn giản4Kế thừa
Kế thừa cơ bản (extends)Từ khóa superGhi đè phương thức (@Override)Chuỗi gọi hàm khởi tạoLớp ObjectKế thừa đơn và đa cấpVì sao không hỗ trợ kế thừa nhiều lớpÔn tập: Phân cấp lớp Employee7Phương thức đặc biệt và lớp Object
Phương thức toString()equals() và hashCode()Phương thức clone()compareTo() và ComparableGiao diện ComparatorÔn tập: sắp xếp tùy chỉnh2Bộ điều chỉnh quyền truy cập và đóng gói
Tổng quan về các mức truy cậpPhương thức getter và setterẨn thông tinTừ khóa finalÔn tập: Trình quản lý tài khoản ngân hàng5Tính đa hình
Kiến thức cơ bản về nạp chồng phương thứcGhi đè phương thức (thời gian chạy)Chuyển kiểu lên và chuyển kiểu xuốngToán tử instanceofLớp và phương thức trừu tượngÔn tập: máy tính hình học8Khái niệm OOP nâng cao
Kết hợp và kế thừaKết tập và hợp thànhLớp bên trong, lớp lồng nhau và lớp ẩn danhKiểu enum và phương thức enumBản ghi (Java 16+)Lớp sealed (Java 17+)3Thuộc tính lớp và thành viên tĩnh
Biến của instance và biến tĩnhPhương thức tĩnhKhối tĩnhHằng số (static final)Ôn tập: bộ đếm và tiện ích6Giao diện và lớp trừu tượng
Giới thiệu về giao diệnTriển khai giao diệnTriển khai nhiều giao diệnPhương thức mặc định và tĩnh trong giao diệnLớp trừu tượng và giao diệnGiao diện hàmÔn tập: hệ thống thanh toán9Kiểu tổng quát
Giới thiệu về kiểu tổng quátLớp tổng quátPhương thức tổng quátTham số kiểu có giới hạnKý tự đại diện (?, extends, super)Ôn tập: vùng chứa tổng quát12Mẫu thiết kế, phần 2
Mẫu CommandMẫu AdapterMẫu DecoratorMẫu Template MethodMẫu StateMẫu CompositeMẫu IteratorTự luyện tập: Trình biên dịch Java online