1. Giới thiệu
Trong lập trình, bạn thường phải làm việc với các danh sách những lựa chọn đã biết và giới hạn sẵn. Ví dụ: ngày trong tuần, màu đèn giao thông, mức độ khó của trò chơi, trạng thái đơn hàng. Mỗi lựa chọn thường được biểu diễn bằng một giá trị cố định. Đây chính là lúc chúng ta tiến gần hơn tới chủ đề hôm nay — kiểu liệt kê (enum).
Kiểu liệt kê là một kiểu dữ liệu đặc biệt cho phép khai báo một tập các hằng có tên.
enum giống như một chiếc hộp với các nhãn được xếp vào từng ngăn, nơi mỗi nhãn là một tên duy nhất gắn với một con số.
Ưu điểm chính của enum:
- Mã của bạn trở nên dễ đọc và tự mô tả;
- Thay vì những “con số ma thuật” như 0, 1, 2, bạn có các tên có ý nghĩa: DayOfWeek.MONDAY hoặc TrafficLight.RED;
- Trình biên dịch sẽ báo lỗi nếu bạn cố gán một giá trị không hợp lệ;
- Đằng sau mỗi phần tử thực ra là… một số nguyên bình thường! (Chúng ta sẽ quay lại chỗ này sau.)
Cú pháp khai báo enum
// Mẫu chung để khai báo enum
public enum Imya
{
KONSTANTA1 ,
KONSTANTA2 ,
KONSTANTA3
}
Khai báo enum trong Java rất đơn giản. Đây là ví dụ cơ bản:
// Tệp DayOfWeek.java
public enum DayOfWeek {
MONDAY, // 0
TUESDAY, // 1
WEDNESDAY, // 2
THURSDAY, // 3
FRIDAY, // 4
SATURDAY, // 5
SUNDAY // 6
}
Giải thích:
- Từ khóa enum nói với trình biên dịch: “Đây là một enum!”
- Trong dấu ngoặc nhọn, các tên lựa chọn (hằng) được liệt kê, ngăn cách bằng dấu phẩy.
- Quy ước đặt tên theo UPPER_SNAKE_CASE (tất cả chữ in hoa, các từ nối bằng dấu gạch dưới).
Có thể khai báo enum ở đâu?
- Trong một tệp riêng (DayOfWeek.java).
- Bên trong lớp (nếu cần enum riêng tư chỉ dùng cho lớp đó).
- Thậm chí bên trong phương thức (nhưng hiếm khi làm vậy).
2. Sử dụng enum trong mã
Khai báo biến enum:
DayOfWeek today = DayOfWeek.MONDAY;
Bây giờ today chỉ có thể nhận một trong các giá trị được khai báo trong DayOfWeek.
Ví dụ sử dụng trong switch
switch (today)
{
case MONDAY:
System.out.println("Ôi, thứ Hai...");
break;
case FRIDAY:
System.out.println("Yeah, thứ Sáu!");
break;
default:
System.out.println("Một ngày bình thường.");
}
Điểm khác biệt quan trọng: trong case chỉ viết tên hằng (không có tiền tố) nếu kiểu của biến đã được trình biên dịch biết.
Duyệt tất cả các giá trị của enum
Mỗi enum có phương thức tĩnh values() trả về một mảng chứa tất cả các giá trị của nó:
for (DayOfWeek day : DayOfWeek.values())
{
System.out.println(day);
}
Rất tiện cho việc in menu, kiểm tra, sinh phương án, v.v.
3. Các phương thức của enum: name(), ordinal(), valueOf()
Enum trong Java không chỉ là danh sách hằng số, mà còn là các lớp thực thụ với phương thức!
Phương thức name()
Trả về tên hằng ở dạng chuỗi (đúng như viết trong mã):
DayOfWeek day = DayOfWeek.FRIDAY;
System.out.println(day.name()); // "FRIDAY"
Phương thức ordinal()
Trả về chỉ số thứ tự (bắt đầu từ 0):
System.out.println(DayOfWeek.MONDAY.ordinal()); // 0
System.out.println(DayOfWeek.FRIDAY.ordinal()); // 4
Lưu ý: Không nên lưu trữ hoặc so sánh enum theo ordinal() nếu thứ tự có thể thay đổi trong tương lai! Tốt hơn hãy dùng tên.
Phương thức valueOf(String name)
Chuyển chuỗi thành giá trị enum nếu có tồn tại hằng như vậy:
DayOfWeek day = DayOfWeek.valueOf("MONDAY");
System.out.println(day); // MONDAY
Nếu chuỗi không khớp — sẽ ném ngoại lệ IllegalArgumentException.
Phương thức values()
Trả về mảng tất cả giá trị của enum (đã có ví dụ ở trên):
DayOfWeek[] days = DayOfWeek.values();
4. Enum với trường, constructor và phương thức
Có thể mở rộng enum trong Java: thêm trường, constructor, phương thức! Đúng vậy, nó không chỉ là danh sách tên, mà còn là một mini-class.
Ví dụ: ngày trong tuần kèm tên bản địa hóa
public enum DayOfWeek
{
MONDAY("Ponedel’nik"),
TUESDAY("Vtornik"),
WEDNESDAY("Sreda"),
THURSDAY("Chetverg"),
FRIDAY("Pyatnitsa"),
SATURDAY("Subbota"),
SUNDAY("Voskresen’e");
private final String russianName;
// Constructor (mặc định là private)
DayOfWeek(String russianName)
{
this.russianName = russianName;
}
public String getRussianName()
{
return russianName;
}
}
Chi tiết cách hoạt động bạn sẽ biết khi chúng ta bắt đầu học OOP ở các mức 27+ :P
Giờ có thể làm như sau:
DayOfWeek day = DayOfWeek.WEDNESDAY;
System.out.println(day.getRussianName()); // "Sreda"
Điểm hay: Mỗi giá trị enum có dữ liệu riêng (trong trường hợp này — tên tiếng Nga).
Ưu điểm khi dùng enum
- An toàn kiểu: không thể vô tình gán cho biến enum một giá trị không có trong danh sách.
- Dễ đọc: mã tự mô tả (TrafficLight.RED thay vì 1 hoặc "red").
- Dễ bảo trì: nếu cần thêm hoặc xóa lựa chọn, chỉ làm ở một nơi.
- Hoạt động với switch: enum rất phù hợp cho rẽ nhánh theo giá trị.
- Có thể mở rộng: thêm trường, phương thức, triển khai interface.
- Ít lỗi hơn: trình biên dịch giúp bạn không quên xử lý các trường hợp (đặc biệt trong cấu trúc switch).
6. Thực hành: dùng enum trong ứng dụng của chúng ta
Hãy thêm vào ứng dụng mini của chúng ta hỗ trợ ngày trong tuần thông qua enum.
Bước 1. Khai báo enum
public enum DayOfWeek
{
MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY
}
Bước 2. Sử dụng trong mã
System.out.println("Nhập ngày trong tuần (ví dụ: MONDAY):");
Scanner console = new Scanner(System.in);
String input = console.nextLine().toUpperCase();
DayOfWeek day = DayOfWeek.valueOf(input);
switch (day)
{
case MONDAY:
System.out.println("Bắt đầu tuần làm việc!");
break;
case FRIDAY:
System.out.println("Cuối tuần sắp tới!");
break;
case SATURDAY:
case SUNDAY:
System.out.println("Yeah, cuối tuần!");
break;
default:
System.out.println("Một ngày làm việc bình thường.");
}
Chuyện gì đang xảy ra:
- Người dùng nhập ngày trong tuần (ví dụ "monday" hoặc "MONDAY").
- Chuyển chuỗi sang chữ hoa (toUpperCase()) để không phụ thuộc vào kiểu chữ.
- Chuyển chuỗi thành giá trị enum qua valueOf.
- Dùng switch để in ra thông điệp khác nhau.
- Nếu nhập không đúng — bắt lỗi và in cảnh báo.
7. Lỗi thường gặp khi làm việc với enum
Lỗi №1: Dùng chuỗi hoặc số thay vì enum. Rất thường gặp khi người mới tiếp tục viết như sau:
int day = 1; // 1 — là thứ Hai?
hoặc
String status = "DELIVERED";
Điều này không tốt: dễ gõ sai, khó bảo trì. Hãy dùng enum!
Lỗi №2: So sánh enum bằng == và .equals(). Trong Java, các giá trị enum có thể so sánh bằng == — điều này an toàn, vì mọi giá trị là duy nhất trong phạm vi lớp enum:
if (day == DayOfWeek.MONDAY) { ... } // OK!
Dù .equals() cũng hoạt động, nhưng thường dùng == hơn.
Lỗi №3: Đặt tên phần tử enum kém. Đừng đặt tên phần tử là VALUE1, VALUE2, VALUE3 — một tháng sau chẳng ai hiểu ý nghĩa của chúng là gì! Hãy dùng tên có ý nghĩa.
Lỗi №4: Dùng ordinal() cho logic. Đừng viết như sau:
if (day.ordinal() == 0) { ... } // Đừng làm vậy!
Thứ tự phần tử có thể thay đổi và mọi thứ sẽ hỏng. Hãy dùng tên!
GO TO FULL VERSION