Sealed class trong Java và cạm bẫy của pattern matching vét cạn

Công nghệ18 tháng 10, 2026·4 phút đọc

Sealed class từ Java 17 giúp trình biên dịch kiểm tra tính vét cạn (exhaustiveness) của switch, loại bỏ nhu cầu dùng nhánh default. Tuy nhiên, khi đưa kiểu sealed vào API công khai, việc thêm một subtype mới có thể âm thầm phá vỡ mã nguồn của bên sử dụng và chỉ lộ ra dưới dạng lỗi runtime.

Sealed class trong Java và cạm bẫy của pattern matching vét cạn

Sealed class trong Java và cạm bẫy của pattern matching vét cạn

Sealed class xuất hiện từ Java 17 như một cải tiến đáng chú ý của ngôn ngữ, cho phép lập trình viên liệt kê tường minh những kiểu con được phép kế thừa một interface hay lớp cơ sở. Đi kèm với đó là khả năng kiểm tra tính vét cạn (exhaustiveness) khi dùng switch, giúp trình biên dịch phát hiện sớm các trường hợp bị bỏ sót.

Tuy nhiên, theo phân tích của Neil Madden, công cụ mạnh mẽ này lại ẩn chứa những rủi ro tinh vi, đặc biệt khi được đưa vào API công khai của thư viện.

Sealed class hoạt động như thế nào?

Với một interface sealed, lập trình viên có thể khai báo rõ những kiểu con được phép. Các record lồng bên trong sẽ tự động được thêm vào danh sách nếu không có khai báo tường minh.

Mô hình này khá giống với khai báo algebraic datatype trong các ngôn ngữ hàm như Haskell hay ML. Điểm mấu chốt là trình biên dịch biết chính xác tập hợp các trường hợp có thể xảy ra.

Sức mạnh của switch vét cạn

Lợi ích lớn nhất nằm ở chỗ switch expression không cần nhánh default. Nếu đã bao phủ đủ mọi kiểu con, trình biên dịch chấp nhận. Nếu thiếu một trường hợp, bạn nhận lỗi ngay khi biên dịch — một lớp bảo vệ tĩnh rất giá trị.

Đây là điểm hấp dẫn khiến nhiều lập trình viên Java yêu thích sealed class, nhất là những người quen với mô hình bất biến của functional programming.

Vấn đề: thêm subtype là một breaking change

Chính đặc tính trên lại tạo ra một kiểu phá vỡ tương thích mới. Khi bạn thêm một subtype vào sealed interface, mọi đoạn mã sử dụng switch vét cạn mà thiếu nhánh xử lý mới sẽ không còn biên dịch được.

Nếu theo đúng SemVer, việc bổ sung subtype vào một kiểu sealed được xuất khẩu trong API phải được coi là thay đổi phá vỡ tương thích và cần tăng major version.

Hiểm họa từ biên dịch tách rời

Vấn đề còn nghiêm trọng hơn: Java chỉ báo lỗi biên dịch khi phía sử dụng (consumer) được biên dịch lại. Nếu bạn chỉ biên dịch lại định nghĩa sealed interface mà không biên dịch lại lớp Main đã dùng nó, mọi thứ vẫn trôi qua bình thường.

Khi chạy với một trường hợp mới chưa được xử lý, chương trình sẽ ném ra MatchException tại runtime thay vì lỗi lúc biên dịch. Tài liệu Javadoc cũng ghi nhận vấn đề này cùng một số trường hợp góc liên quan đến giá trị null trong pattern.

Hệ quả là kỳ vọng "sealed class đảm bảo tĩnh rằng mọi consumer đều được cập nhật" không hề được đáp ứng, trừ khi toàn bộ dự án được biên dịch lại đồng bộ.

Khuyến nghị cho lập trình viên và các đội phát triển

Từ những phân tích trên, có thể rút ra kết luận thực dụng:

  • Dùng sealed type trong nội bộ thành phần là lựa chọn hợp lý, vì ở đó bạn thường biên dịch toàn bộ cùng lúc và tận hưởng trọn vẹn lợi ích kiểm tra vét cạn.
  • Hạn chế đưa sealed type vào API công khai cho các thành phần khác hoặc cho thư viện, vì bạn có thể đang tạo ra những cách thức phá vỡ mã mới, thường chỉ phát hiện được khi chạy thực tế.
  • Cân nhắc kỹ phiên bản nếu buộc phải mở rộng một kiểu sealed đã công bố.

Tác giả cũng chia sẻ rằng ông phát hiện những tinh tế này khi đánh giá tính năng xem trước hỗ trợ đối tượng mật mã mã hóa PEM — một thiết kế mắc đúng sai lầm khi gắn cứng một sealed interface vào API công khai và khuyến khích phía client pattern match trực tiếp trên kiểu đó. Ông dự đoán khả năng phá vỡ rất cao nếu họ muốn thêm trường hợp mới trong tương lai.

Góc nhìn cho cộng đồng Java Việt Nam

Với các đội phát triển phần mềm trong nước đang chuyển dần lên Java 17 và 21, sealed class cùng pattern matching là công cụ đáng học và áp dụng. Bài học ở đây không phải là né tránh tính năng này, mà là hiểu rõ ranh giới: hãy dùng nó như vũ khí nội bộ để tăng độ an toàn kiểu, và thận trọng khi biến nó thành hợp đồng công khai với bên ngoài.

Chia sẻ:FacebookX
Nội dung tổng hợp bằng AI, mang tính tham khảo. Xem bài gốc ↗