Lớp sealed (Java 17+)
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 52 trên 87.
Trong Java truyền thống, khi tạo một lớp, bất kỳ lớp nào khác cũng có thể kế thừa lớp đó, trừ khi bạn đánh dấu lớp đó là final. Nhưng nếu bạn muốn cho phép kế thừa, nhưng không phải từ bất kỳ lớp nào thì sao? Java 17 đã giới thiệu lớp sealed để bạn kiểm soát chính xác những lớp nào có thể kế thừa lớp của mình.
Một lớp sealed khai báo rõ các lớp con được phép của nó bằng cách sử dụng từ khóa sealed và permits:
sealed abstract class Shape permits Circle, Rectangle, Triangle {
abstract double area();
}
final class Circle extends Shape {
double radius;
double area() { return Math.PI * radius * radius; }
}
final class Rectangle extends Shape {
double width, height;
double area() { return width * height; }
}
final class Triangle extends Shape {
double base, height;
double area() { return 0.5 * base * height; }
}Chỉ Circle, Rectangle và Triangle mới có thể kế thừa Shape. Bất kỳ lớp nào khác cố gắng kế thừa lớp này sẽ gây ra lỗi biên dịch.
Mỗi lớp con được phép phải dùng một trong ba bổ ngữ: final (không thể mở rộng thêm), sealed (tiếp tục chuỗi hạn chế) hoặc non-sealed (cho phép mở rộng không hạn chế):
sealed class Vehicle permits Car, Truck { }
final class Car extends Vehicle { } // Không thể kế thừa lớp này
non-sealed class Truck extends Vehicle { } // Ai cũng có thể kế thừa TruckLớp được niêm phong đặc biệt hữu ích khi kết hợp với so khớp mẫu trong biểu thức switch, vì trình biên dịch biết tất cả các kiểu con có thể có và có thể kiểm tra tính đầy đủ. Chúng rất phù hợp để mô hình hóa một tập hợp kiểu cố định trong một lĩnh vực, chẳng hạn như phương thức thanh toán, kiểu phản hồi hoặc hình dạng hình học.
Thử thách
DễHãy xây dựng một hệ thống thông báo để minh họa lớp sealed bằng cách giới hạn những kiểu thông báo có thể tồn tại trong ứng dụng của bạn. Bạn sẽ tạo một hệ thống phân cấp, trong đó chỉ một số kiểu thông báo cụ thể được phép tồn tại, và mỗi kiểu xử lý việc gửi thông báo theo cách khác nhau.
Bạn sẽ sắp xếp code trong bốn tệp:
Notification.java: Tạo một lớp trừu tượng sealed làm lớp cơ sở cho mọi thông báo. Lớp này chỉ được phép có đúng ba lớp con:EmailNotification,SMSNotificationvàPushNotification. Lớp sealed của bạn cần có một trường protectedmessage(String), một hàm khởi tạo để khởi tạo trường này, một hàm getter cho message và một hàm trừu tượngdeliver()trả về một String mô tả cách gửi thông báo.EmailNotification.java: Tạo một lớpfinalkế thừa Notification. EmailNotification có thêm trường privaterecipient(String) để lưu địa chỉ email. Hàm khởi tạo nhận cả message và recipient, đồng thời gọi hàm khởi tạo của lớp cha bằngsuper(). Hàmdeliver()sẽ trả về:Sending email to [recipient]: [message]SMSNotification.java: Tạo một lớpnon-sealedkế thừa Notification. Điều này cho phép các lớp khác kế thừa lớp này nếu cần trong tương lai. SMSNotification có thêm trường privatephoneNumber(String). Hàm khởi tạo nhận message và số điện thoại. Hàmdeliver()sẽ trả về:Sending SMS to [phoneNumber]: [message]PushNotification.java: Tạo một lớpfinalkế thừa Notification. PushNotification có thêm trường privatedeviceId(String). Hàm khởi tạo nhận message và ID thiết bị. Hàmdeliver()sẽ trả về:Sending push to device [deviceId]: [message]Main.java: Kết hợp các phần của hệ thống thông báo! Bạn sẽ nhận bốn đầu vào: một message (String), một địa chỉ email (String), một số điện thoại (String) và một ID thiết bị (String).Tạo mỗi kiểu thông báo một lần, dùng cùng một message nhưng với thông tin liên hệ tương ứng. Lưu cả ba vào một mảng có kiểu
Notification[]để minh họa tính đa hình với các lớp sealed. Sau đó, duyệt qua mảng và in ra kết quả gọideliver()trên từng thông báo.
Bạn sẽ nhận bốn đầu vào theo thứ tự: message, địa chỉ email, số điện thoại và ID thiết bị.
Hãy chú ý cách lớp sealed giới hạn hệ thống phân cấp: chỉ ba lớp được phép mới có thể kế thừa Notification. Mỗi lớp con phải khai báo là final, sealed hoặc non-sealed. Nhờ đó, bạn có toàn quyền kiểm soát hệ thống phân cấp kiểu mà vẫn có thể sử dụng tính đa hình!
Tự làm thử
import java.util.Scanner;
class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
// Đọc bốn đầu vào
String message = scanner.nextLine();
String email = scanner.nextLine();
String phoneNumber = scanner.nextLine();
String deviceId = scanner.nextLine();
// TODO: Tạo một thông báo thuộc mỗi loại bằng cùng một message
// EmailNotification với message và email
// SMSNotification với message và phoneNumber
// PushNotification với message và deviceId
// TODO: Lưu cả ba thông báo vào mảng (array) Notification[]
// Đây là ví dụ về tính đa hình với các lớp niêm phong
// TODO: Duyệt qua mảng và in ra kết quả của deliver()
// cho từng thông báo
}
}
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