
Enum trong Java: Khi các 'Thẻ Bài' định nghĩa thế giới của bạn
Chào các chiến thần GenZ, lại là anh Creyt đây! Hôm nay chúng ta sẽ 'khui' một khái niệm nghe hơi học thuật nhưng lại cực kỳ 'bá đạo' trong Java, đó là Enum. Nghe tên thì khô khan, nhưng tin anh đi, nó sẽ là 'gia vị' giúp code của em 'ngon' hơn, ít 'bug' hơn và 'clean' hơn rất nhiều.
1. Enum là gì và để làm gì? (Giải mã GenZ Style)
Thế này nhé, em cứ tưởng tượng thế giới code của chúng ta đôi khi cần những giá trị 'bất biến', tức là nó chỉ có thể là một trong số ít các lựa chọn cố định. Ví dụ:
- Trạng thái đơn hàng: CHỜ_XÁC_NHẬN, ĐANG_GIAO, ĐÃ_GIAO, ĐÃ_HỦY.
- Các ngày trong tuần: THỨ_HAI, THỨ_BA, ... CHỦ_NHẬT.
- Cấp độ người dùng: ADMIN, MODERATOR, USER, GUEST.
Trước đây, có thể em sẽ dùng int (0, 1, 2, 3...) hoặc String ("PENDING", "DELIVERED"...) để đại diện cho mấy cái này. Nhưng mà, dùng int thì dễ nhầm lẫn (số 5 là gì?); dùng String thì dễ gõ sai chính tả (chữ "PENDING" thành "PENDINGG" là toang!). Đó là lúc Enum (viết tắt của Enumeration) xuất hiện như một 'siêu anh hùng'.
Enum là một kiểu dữ liệu đặc biệt cho phép em định nghĩa một tập hợp các hằng số (constants) có tên. Nó giống như việc em tạo ra một bộ 'thẻ bài Yu-Gi-Oh' với những tên gọi rõ ràng, không thể nhầm lẫn và chỉ có bấy nhiêu lá bài thôi. Không ai có thể tự tiện tạo ra một lá bài mới ngoài bộ đó cả. Nhờ vậy:
- An toàn kiểu dữ liệu (Type Safety): Code của em chỉ chấp nhận những giá trị đã được định nghĩa. Không có chuyện nhập bừa 'trạng thái 99' hay 'ngày mai kia' vào được.
- Dễ đọc (Readability): Thay vì
if (status == 1), em cóif (status == TrangThaiDonHang.DANG_GIAO). Đọc phát hiểu luôn, không cần đoán mò. - Dễ bảo trì (Maintainability): Nếu sau này có thêm trạng thái mới, em chỉ cần thêm vào
Enumlà xong, không cần mò mẫm khắp nơi sửainthayString.
2. Code Ví Dụ Minh Họa: 'Triệu hồi' Enum
Ví dụ cơ bản: Các ngày trong tuần
Bắt đầu với cái dễ nhất: các ngày trong tuần. Chúng ta có 7 ngày, cố định, không thêm không bớt.
// Bước 1: Định nghĩa Enum của bạn
public enum NgayTrongTuan {
THU_HAI, // Đây là một hằng số Enum
THU_BA,
THU_TU,
THU_NAM,
THU_SAU,
THU_BAY,
CHU_NHAT
}
// Bước 2: Sử dụng Enum trong code
public class LichLamViec {
public static void main(String[] args) {
NgayTrongTuan homNay = NgayTrongTuan.THU_TU;
System.out.println("Hôm nay là: " + homNay);
// Dùng switch-case với Enum cực kỳ hiệu quả
switch (homNay) {
case THU_BAY:
case CHU_NHAT:
System.out.println("Yay! Cuối tuần rồi, đi chơi thôi!");
break;
case THU_SAU:
System.out.println("Gần cuối tuần rồi, cố lên!");
break;
default:
System.out.println("Lại phải đi học/làm rồi T_T");
}
// Vòng lặp qua tất cả các giá trị của Enum
System.out.println("\n--- Tất cả các ngày trong tuần ---");
for (NgayTrongTuan ngay : NgayTrongTuan.values()) {
System.out.println(ngay);
}
}
}
Kết quả sẽ là:
Hôm nay là: THU_TU
Lại phải đi học/làm rồi T_T
--- Tất cả các ngày trong tuần ---
THU_HAI
THU_BA
THU_TU
THU_NAM
THU_SAU
THU_BAY
CHU_NHAT
Thấy chưa? Rõ ràng, dễ hiểu, không sợ gõ nhầm.
Ví dụ nâng cao: Enum với thuộc tính và phương thức
Đừng coi thường Enum nhé! Nó không chỉ là tập hợp các hằng số vô tri đâu. Thực chất, mỗi hằng số trong Enum là một đối tượng (object) và bản thân Enum cũng là một class đặc biệt. Điều này có nghĩa là em có thể thêm các thuộc tính (fields), constructor và phương thức (methods) cho các hằng số Enum của mình. Nghe 'khét' chưa?
Hãy tưởng tượng TrangThaiDonHang không chỉ có tên mà còn có một mô tả tiếng Việt và một phương thức để kiểm tra xem trạng thái đó có thể chuyển sang trạng thái tiếp theo được không.
public enum TrangThaiDonHang {
CHO_XAC_NHAN("Đơn hàng đang chờ xác nhận.", true), // Constructor được gọi ở đây
DANG_GIAO("Đơn hàng đang trên đường đến bạn.", true),
DA_GIAO("Đơn hàng đã được giao thành công.", false),
DA_HUY("Đơn hàng đã bị hủy.", false);
// Thuộc tính của mỗi hằng số Enum
private final String moTa;
private final boolean coTheChuyenTiep;
// Constructor riêng cho Enum
// Lưu ý: Constructor của Enum luôn là private hoặc package-private
private TrangThaiDonHang(String moTa, boolean coTheChuyenTiep) {
this.moTa = moTa;
this.coTheChuyenTiep = coTheChuyenTiep;
}
// Phương thức để lấy mô tả
public String getMoTa() {
return moTa;
}
// Phương thức kiểm tra khả năng chuyển tiếp trạng thái
public boolean isCoTheChuyenTiep() {
return coTheChuyenTiep;
}
// Một phương thức ví dụ để chuyển trạng thái (đơn giản hóa)
public TrangThaiDonHang nextState() {
return switch (this) {
case CHO_XAC_NHAN -> DANG_GIAO;
case DANG_GIAO -> DA_GIAO;
case DA_GIAO, DA_HUY -> this; // Không thể chuyển tiếp từ các trạng thái này
};
}
}
public class QuanLyDonHang {
public static void main(String[] args) {
TrangThaiDonHang donHang1 = TrangThaiDonHang.CHO_XAC_NHAN;
TrangThaiDonHang donHang2 = TrangThaiDonHang.DANG_GIAO;
TrangThaiDonHang donHang3 = TrangThaiDonHang.DA_GIAO;
System.out.println("\n--- Trạng thái đơn hàng ---");
System.out.println("Đơn hàng 1: " + donHang1.getMoTa());
System.out.println("Có thể chuyển tiếp: " + donHang1.isCoTheChuyenTiep());
System.out.println("Trạng thái tiếp theo (nếu có): " + donHang1.nextState().getMoTa());
System.out.println("\nĐơn hàng 2: " + donHang2.getMoTa());
System.out.println("Có thể chuyển tiếp: " + donHang2.isCoTheChuyenTiep());
System.out.println("Trạng thái tiếp theo (nếu có): " + donHang2.nextState().getMoTa());
System.out.println("\nĐơn hàng 3: " + donHang3.getMoTa());
System.out.println("Có thể chuyển tiếp: " + donHang3.isCoTheChuyenTiep());
System.out.println("Trạng thái tiếp theo (nếu có): " + donHang3.nextState().getMoTa());
// Tìm một Enum constant từ String
String trangThaiString = "DANG_GIAO";
try {
TrangThaiDonHang timThay = TrangThaiDonHang.valueOf(trangThaiString);
System.out.println("\nTìm thấy trạng thái từ String: " + timThay.getMoTa());
} catch (IllegalArgumentException e) {
System.out.println("Không tìm thấy trạng thái: " + trangThaiString);
}
}
}
Output:
--- Trạng thái đơn hàng ---
Đơn hàng 1: Đơn hàng đang chờ xác nhận.
Có thể chuyển tiếp: true
Trạng thái tiếp theo (nếu có): Đơn hàng đang trên đường đến bạn.
Đơn hàng 2: Đơn hàng đang trên đường đến bạn.
Có thể chuyển tiếp: true
Trạng thái tiếp theo (nếu có): Đơn hàng đã được giao thành công.
Đơn hàng 3: Đơn hàng đã được giao thành công.
Có thể chuyển tiếp: false
Trạng thái tiếp theo (nếu có): Đơn hàng đã được giao thành công.
Tìm thấy trạng thái từ String: Đơn hàng đang trên đường đến bạn.
Giờ thì mỗi 'thẻ bài' của em không chỉ có tên mà còn có 'hiệu ứng' riêng, 'chỉ số' riêng, tha hồ mà 'build deck' cho code!

3. Mẹo (Best Practices) của anh Creyt để 'chiến' Enum
- Đặt tên chuẩn chỉ: Các hằng số Enum nên viết HOA_TẤT_CẢ và dùng dấu gạch dưới (
_) để phân tách từ (UPPER_SNAKE_CASE). Ví dụ:TRANG_THAI_DON_HANG,LOAI_SAN_PHAM. - Dùng khi nào? Chỉ dùng
Enumkhi em có một tập hợp giá trị cố định, hữu hạn và có liên quan đến nhau. Nếu giá trị có thể thay đổi liên tục hoặc quá nhiều thì cân nhắc dùng cách khác (ví dụ: database). - Đừng lạm dụng:
Enumlà class, nhưng nó không phải là giải pháp thay thế cho mọiclasshayinterface. Đừng cố nhét quá nhiều logic phức tạp vàoEnumnếu nó làm code khó đọc hơn. valueOf()vàvalues()là bạn thân: Nhớ hai phương thức tĩnh này nhé.values()trả về một mảng chứa tất cả các hằng số Enum.valueOf(String name)sẽ trả về hằng số Enum có tên trùng vớiname(nhớ là phải khớp chính xác, không thì nó némIllegalArgumentExceptionđấy).switchstatement:Enumvàswitchlà một cặp trời sinh. DùngswitchvớiEnumgiúp code của em cực kỳ gọn gàng và dễ đọc.- Tránh
null: Các hằng số Enum không bao giờ lànull, điều này giúp giảm thiểu lỗiNullPointerException(một trong những lỗi 'khó chịu' nhất).
4. Ứng dụng thực tế: Enum 'phủ sóng' mọi nơi
Em có để ý không, Enum được dùng ở khắp mọi nơi trong các ứng dụng/website mà em dùng hàng ngày đấy:
- Các trang thương mại điện tử (Shopee, Tiki, Lazada): Dùng
EnumchoTrangThaiDonHang,LoaiThanhToan(Tiền mặt, Chuyển khoản, Thẻ tín dụng),TrangThaiGiaoHang. - Các mạng xã hội (Facebook, Instagram): Dùng cho
LoaiBaiViet(Ảnh, Video, Text),TrangThaiQuanHe(Độc thân, Đã kết hôn),QuyenNguoiDung(Admin, Member, Viewer). - Các game online:
TrangThaiGame(Đang chơi, Tạm dừng, Kết thúc),LoaiVatPham(Vũ khí, Giáp, Thuốc),HuongDiChuyen(Lên, Xuống, Trái, Phải). - Ngân hàng điện tử:
LoaiGiaoDich(Chuyển tiền, Rút tiền, Thanh toán hóa đơn),TrangThaiGiaoDich(Thành công, Thất bại, Đang xử lý).
Thấy chưa, nó không phải là thứ gì xa vời đâu, nó là 'xương sống' của rất nhiều tính năng mà em đang dùng đó.
5. Thử nghiệm của anh Creyt và khi nào nên dùng?
Anh Creyt đã 'chinh chiến' với Enum từ hồi mới ra lò, và kinh nghiệm xương máu là:
NÊN DÙNG Enum khi:
- Giá trị cố định và biết trước: Khi em có một tập hợp các giá trị không đổi và em biết tất cả chúng ngay từ đầu (ví dụ: các mùa trong năm, các hằng số toán học).
- Cần an toàn kiểu dữ liệu: Khi em muốn đảm bảo rằng biến chỉ nhận một trong các giá trị hợp lệ đã định nghĩa, tránh lỗi do nhập sai hoặc giá trị không mong muốn.
- Muốn code dễ đọc và bảo trì: Thay vì các 'magic numbers' (số bí ẩn) hay 'magic strings' (chuỗi bí ẩn),
Enummang lại ý nghĩa rõ ràng cho code. - Khi các hằng số có thêm logic riêng: Như ví dụ
TrangThaiDonHangở trên, khi mỗi hằng số cần có thuộc tính hoặc phương thức riêng để xử lý logic cụ thể.
KHÔNG NÊN DÙNG Enum khi:
- Giá trị quá động: Nếu tập hợp các giá trị thay đổi liên tục hoặc được tạo ra từ dữ liệu bên ngoài (ví dụ: danh sách khách hàng từ database), thì
Enumkhông phải là lựa chọn tốt. Lúc đó hãy dùng cácList,MaphoặcClassbình thường. - Số lượng giá trị quá lớn: Nếu em có hàng trăm, hàng ngàn giá trị, việc định nghĩa chúng trong
Enumsẽ làm file code rất lớn và khó quản lý. Hãy nghĩ đến database hoặc cấu hình file.
Vậy đó, Enum là một 'công cụ' cực kỳ mạnh mẽ trong Java, giúp code của em không chỉ chạy đúng mà còn 'đẹp mã', 'dễ hiểu' và 'bền vững' hơn. Cứ luyện tập và áp dụng vào các dự án của mình, em sẽ thấy nó 'thần kỳ' đến mức nào!
Chúc các GenZ code 'sung', 'thăng hoa' và sớm trở thành 'master' nhé! Peace out! Peace!
Thuộc Series: Java – OOP
Bài giảng này được tự động xuất bản ngẫu nhiên từ thư viện kiến thức. Đừng quên đón xem các Từ khoá Hướng Dẫn tiếp theo nhé!