99 Ngày Java — Ngày 23: Enum
Ngày 22 khép lại với lời hẹn về kiểu dữ liệu mô hình hóa tập giá trị cố định — hôm nay ta gặp enum. Trạng thái đơn hàng, phương thức thanh toán, ngày trong tuần: những giá trị đếm được trên đầu ngón tay xứng đáng có kiểu riêng thay vì hằng số int hay String rời rạc. Và enum trong Java không dừng ở danh sách tên: nó là lớp đầy đủ với field, constructor, method — kèm hai vũ khí ít người tận dụng là switch exhaustive và bộ đôi EnumMap/EnumSet.

Enum cơ bản — tập giá trị cố định, an toàn kiểu
// Enum — kiểu liệt kê: tập giá trị CỐ ĐỊNH, biết trước khi biên dịch
public enum TrangThaiDonHang {
MOI, DA_XAC_NHAN, DANG_GIAO, HOAN_TAT, DA_HUY
}
TrangThaiDonHang tt = TrangThaiDonHang.DANG_GIAO;
// Mỗi hằng số là MỘT đối tượng duy nhất do JVM tạo sẵn —
// nên == an toàn tuyệt đối (trái ngược wrapper của Ngày 22!):
tt == TrangThaiDonHang.DANG_GIAO; // ✓ true, luôn luôn
// API có sẵn cho mọi enum:
TrangThaiDonHang.values(); // mảng mọi hằng số, đúng thứ tự khai báo
TrangThaiDonHang.valueOf("MOI"); // chuỗi → hằng số
TrangThaiDonHang.valueOf("XXX"); // ✗ IllegalArgumentException
tt.name(); // "DANG_GIAO"
tt.ordinal(); // 2 — vị trí khai báo; KHÔNG đem lưu trữ,
// chèn hằng số mới là mọi ordinal xô lệchSo với hằng số
int/String: enum an toàn kiểu (không thể gán"DANG_GIAOO"gõ nhầm), tự liệt kê được quavalues(), và debug ra tên dễ đọc thay vì con số vô danh.Mỗi hằng số là singleton do JVM bảo đảm — nên
==với enum luôn đúng, ngược hẳn bài wrapper hôm qua: cùng một phép so sánh, độ tin cậy phụ thuộc vào kiểu dữ liệu đứng sau.ordinal()là chi tiết cài đặt: chèn một hằng số mới vào giữa là mọi vị trí xô lệch — không bao giờ đem lưu DB hay so sánh nghiệp vụ, hãy dùngname().
Enum là lớp đầy đủ — field, constructor, method
public enum PhuongThucThanhToan {
// Mỗi hằng số gọi constructor với dữ liệu riêng của nó:
TIEN_MAT("Tiền mặt", 0),
THE_TIN_DUNG("Thẻ tín dụng", 2.5),
VI_DIEN_TU("Ví điện tử", 1.0);
private final String ten; // field — final: hằng số nên bất biến
private final double phiPhanTram;
// constructor của enum mặc định (và bắt buộc) là private —
// không ai new thêm hằng số từ bên ngoài
PhuongThucThanhToan(String ten, double phiPhanTram) {
this.ten = ten;
this.phiPhanTram = phiPhanTram;
}
public double tinhPhi(double soTien) { // method như lớp thường
return soTien * phiPhanTram / 100;
}
}
PhuongThucThanhToan.THE_TIN_DUNG.tinhPhi(2_000_000); // 50000.0
// Logic đi theo dữ liệu của từng hằng số — không cần bảng tra cứu ngoàiĐây là điểm nâng enum Java lên trên enum của nhiều ngôn ngữ khác: mỗi hằng số mang dữ liệu riêng (tên hiển thị, phí) và hành vi (
tinhPhi) — logic đi theo hằng số thay vì rải rác trong các bảng tra cứu if-else.Constructor của enum bắt buộc private — chính ràng buộc này bảo đảm tập giá trị đóng: toàn bộ hằng số nằm gọn trong file khai báo, không nơi nào tạo thêm được.
Field nên
final: hằng số dùng chung toàn ứng dụng, một chỗ sửa được trạng thái là mọi nơi cùng thấy — bài học bất biến của Ngày 20 áp dụng nguyên vẹn.
switch với enum — exhaustiveness
// Switch expression (Ngày 06) + enum = bộ đôi exhaustive:
String thongBao = switch (trangThai) {
case MOI -> "Đơn vừa tạo, chờ xác nhận";
case DA_XAC_NHAN -> "Đã xác nhận, đang chuẩn bị hàng";
case DANG_GIAO -> "Đang trên đường giao";
case HOAN_TAT, DA_HUY -> "Đơn đã đóng";
}; // đủ mọi hằng số → KHÔNG cần default
// Sức mạnh thật sự nằm ở lúc THAY ĐỔI:
// thêm hằng số DOI_TRA vào enum → mọi switch expression thiếu case
// FAIL NGAY KHI BIÊN DỊCH — compiler liệt kê giúp mọi chỗ cần cập nhật.
// Vì thế: đừng viết default "cho chắc" —
// default nuốt mất cảnh báo quý giá đó.Switch expression đủ mọi case thì không cần default — và đó là lựa chọn có chủ đích: khi enum mọc thêm hằng số, compiler chỉ đích danh từng switch cần bổ sung, một dạng lưới an toàn miễn phí khi refactor.
Viết
default"cho chắc" là đánh đổi tai hại: code chạy tiếp bằng nhánh chung chung thay vì báo lỗi biên dịch — lỗi chỉ lộ ra ở runtime, đúng loại lỗi khó tìm nhất.Kỹ thuật này áp dụng cho cả pattern matching với sealed classes (Ngày 83) — exhaustiveness là chủ đề xuyên suốt Java hiện đại.
EnumMap & EnumSet — collection chuyên dụng
// EnumMap — Map với key là enum: mảng theo ordinal bên trong, gọn và nhanh
Map<TrangThaiDonHang, Long> demTheoTrangThai =
new EnumMap<>(TrangThaiDonHang.class);
demTheoTrangThai.put(TrangThaiDonHang.MOI, 12L);
// EnumSet — Set các hằng số enum: bit vector bên trong, một long 64 bit
EnumSet<TrangThaiDonHang> dangMo =
EnumSet.of(TrangThaiDonHang.MOI, TrangThaiDonHang.DA_XAC_NHAN);
EnumSet<TrangThaiDonHang> daDong = EnumSet.complementOf(dangMo);
dangMo.contains(tt); // O(1) — một phép AND bit
// Lưu enum ra ngoài (DB, JSON): LUÔN theo name(), không theo ordinal —
// với JPA là @Enumerated(EnumType.STRING) (series Spring, Ngày 39)EnumMaplưu value trong mảng đánh chỉ số theo ordinal — không hash, không collision, duyệt đúng thứ tự khai báo; đếm số lượng theo trạng thái là use case điển hình.EnumSetnén cả tập hợp vào một bit vector: phépcontains/complementOfchỉ là thao tác bit — thay thế hoàn hảo cho những chuỗi cờbooleanrời rạc.Quy tắc lưu trữ chốt bài: enum ra ngoài hệ thống luôn theo
name()— với JPA là@Enumerated(EnumType.STRING), chi tiết chờ ở series Spring Ngày 39; bài Spring hôm nay cũng chạm mặt enum trong derived query.
Bài tập nhỏ
Viết enum
NgayTrongTuan7 hằng số kèm fieldlaCuoiTuan(boolean) — thêm methodstaticđếm số ngày làm việc bằngvalues().Mở rộng
PhuongThucThanhToan: thêm hằng sốCHUYEN_KHOANphí 0.5% — quan sát mọi switch expression thiếu case báo lỗi biên dịch ở đâu.Dùng
EnumMapđếm số đơn theoTrangThaiDonHangtừ một mảng trạng thái cho trước — in kết quả đúng thứ tự khai báo.Thử
valueOfvới chuỗi sai tên và bắtIllegalArgumentException— viết methodtimAnToantrả vềOptionalthay vì ném lỗi.
Kết luận
Enum gói trong bốn ý: tập giá trị cố định và an toàn kiểu với == đáng tin (điều wrapper không có), lớp đầy đủ để logic đi theo từng hằng số, switch exhaustive biến compiler thành lưới an toàn khi thay đổi, và EnumMap/EnumSet cho hiệu năng chuyên dụng. Ngày 24 ta xuống nền móng của mọi đối tượng: equals, hashCode và toString — bộ ba contract của Object mà HashMap đặt trọn niềm tin. Hẹn gặp lại!
Site Admin
Engineer and writer. Building things with TypeScript and distributed systems.
Bình luận (0)
Bạn cần đăng nhập bằng Google để bình luận.
Hãy là người bình luận đầu tiên.


