Menu
CoddyTech

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 Truck

Lớ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.

challenge icon

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, SMSNotification và PushNotification. Lớp sealed của bạn cần có một trường protected message (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ượng deliver() trả về một String mô tả cách gửi thông báo.
  • EmailNotification.java: Tạo một lớp final kế thừa Notification. EmailNotification có thêm trường private recipient (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ằng super(). Hàm deliver() sẽ trả về: Sending email to [recipient]: [message]
  • SMSNotification.java: Tạo một lớp non-sealed kế 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 private phoneNumber (String). Hàm khởi tạo nhận message và số điện thoại. Hàm deliver() sẽ trả về: Sending SMS to [phoneNumber]: [message]
  • PushNotification.java: Tạo một lớp final kế thừa Notification. PushNotification có thêm trường private deviceId (String). Hàm khởi tạo nhận message và ID thiết bị. Hàm deliver() 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ọi deliver() 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
        
    }
}
quiz iconTự kiểm tra

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

Tự luyện tập: Trình biên dịch Java online