Enum được giới thiệu trong Java 1.5 như một kiểu dữ liệu mới có các trường bao gồm một tập hợp các hằng số cố định. Ví dụ, chúng ta có thể tạo các directions (hướng) dưới dạng Java Enum với các trường cố định là EAST, WEST, NORTH, và SOUTH.

Enum trong Java
Trong hướng dẫn này, chúng ta sẽ tìm hiểu cách tạo một Enum. Chúng ta cũng sẽ xem xét những lợi ích khi sử dụng enum trong Java và các tính năng của các loại enum. Đồng thời, chúng ta sẽ học cách sử dụng Java Enum với các method valueOf, values, EnumSet và EnumMap thông qua các ví dụ cụ thể.
Ví dụ về Java Enum
Từ khóa enum trong Java được sử dụng để tạo một kiểu enum. Hãy cùng xem xét chương trình ví dụ về Java enum.
package com.journaldev.enums;
public enum ThreadStates {
START,
RUNNING,
WAITING,
DEAD;
}
Chắc chắn rồi! Trong ví dụ trên, ThreadStates là một enum với các trường hằng số cố định là START, RUNNING, WAITING, và DEAD.
Java Enum so với Constants
Bây giờ, hãy cùng xem Java Enum tốt hơn các trường constants thông thường trong các Java class như thế nào. Hãy tạo một class constants tương tự trong Java.
package com.journaldev.enums;
public class ThreadStatesConstant {
public static final int START = 1;
public static final int WAITING = 2;
public static final int RUNNING = 3;
public static final int DEAD = 4;
}
Bây giờ, hãy cùng xem cả enum và constants được sử dụng trong một chương trình Java như thế nào:
/**
* This method shows the benefit of using Enum over Constants
*/
private static void benefitsOfEnumOverConstants() {
//Enum values are fixed
simpleEnumExample(ThreadStates.START);
simpleEnumExample(ThreadStates.WAITING);
simpleEnumExample(ThreadStates.RUNNING);
simpleEnumExample(ThreadStates.DEAD);
simpleEnumExample(null);
simpleConstantsExample(1);
simpleConstantsExample(2);
simpleConstantsExample(3);
simpleConstantsExample(4);
//we can pass any int constant
simpleConstantsExample(5);
}
private static void simpleEnumExample(ThreadStates th) {
if(th == ThreadStates.START) System.out.println("Thread started");
else if (th == ThreadStates.WAITING) System.out.println("Thread is waiting");
else if (th == ThreadStates.RUNNING) System.out.println("Thread is running");
else System.out.println("Thread is dead");
}
private static void simpleConstantsExample(int i) {
if(i == ThreadStatesConstant.START) System.out.println("Thread started");
else if (i == ThreadStatesConstant.WAITING) System.out.println("Thread is waiting");
else if (i == ThreadStatesConstant.RUNNING) System.out.println("Thread is running");
else System.out.println("Thread is dead");
}
Nếu chúng ta nhìn vào ví dụ trên, chúng ta có hai rủi ro khi sử dụng constants mà enum đã giải quyết.
- Chúng ta có thể truyền bất kỳ int constant nào vào method
simpleConstantsExample, nhưng chúng ta chỉ có thể truyền các giá trị cố định vàosimpleEnumExample, do đó nó cung cấp type safety. - Chúng ta có thể thay đổi giá trị của các int constants trong class
ThreadStatesConstantnhưng chương trình trên sẽ không tạo ra bất kỳ exception nào. Chương trình của chúng ta có thể không hoạt động như mong đợi, nhưng nếu chúng ta thay đổi các enum constants, chúng ta sẽ nhận được lỗi compile time loại bỏ mọi khả năng xảy ra các vấn đề runtime.
Các phương thức Enum trong Java
Bây giờ, hãy cùng xem thêm các tính năng của Java enum thông qua một ví dụ.
package com.journaldev.enums;
import java.io.Closeable;
import java.io.IOException;
/**
* This Enum example shows all the things we can do with Enum types
*
*/
public enum ThreadStatesEnum implements Closeable{
START(1){
@Override
public String toString(){
return "START implementation. Priority="+getPriority();
}
@Override
public String getDetail() {
return "START";
}
},
RUNNING(2){
@Override
public String getDetail() {
return "RUNNING";
}
},
WAITING(3){
@Override
public String getDetail() {
return "WAITING";
}
},
DEAD(4){
@Override
public String getDetail() {
return "DEAD";
}
};
private int priority;
public abstract String getDetail();
//Enum constructors should always be private.
private ThreadStatesEnum(int i){
priority = i;
}
//Enum can have methods
public int getPriority(){
return this.priority;
}
public void setPriority(int p){
this.priority = p;
}
//Enum can override functions
@Override
public String toString(){
return "Default ThreadStatesConstructors implementation. Priority="+getPriority();
}
@Override
public void close() throws IOException {
System.out.println("Close of Enum");
}
}
Các điểm quan trọng của Java Enum
- Tất cả Java enum ngầm định mở rộng (implicitly extends)
java.lang.Enumclass, vốn mở rộng Object class và triển khai các interface Serializable và Comparable. Vì vậy, chúng ta không thể mở rộng bất kỳ class nào trong enum. - Vì
enumlà một từ khóa, chúng ta không thể kết thúc tên package bằng nó, ví dụcom.journaldev.enumkhông phải là một tên package hợp lệ. - Enum có thể triển khai các interface. Như trong ví dụ enum trên, nó đang triển khai interface Closeable.
- Các constructor của Enum luôn là private.
- Chúng ta không thể tạo instance của enum bằng toán tử
new. - Chúng ta có thể khai báo các abstract method trong Java enum, khi đó tất cả các enum field phải triển khai abstract method đó. Trong ví dụ trên,
getDetail()là abstract method và tất cả các enum field đã triển khai nó. - Chúng ta có thể định nghĩa một method trong enum và các enum field cũng có thể ghi đè (override) chúng. Ví dụ, method
toString()được định nghĩa trong enum và enum field START đã ghi đè nó. - Các field trong enum có không gian tên riêng, chúng ta chỉ có thể sử dụng enum field với tên class như
ThreadStates.START. - Enum có thể được sử dụng trong câu lệnh
switch.Phần sau của bài viết sẽ minh hoạ cụ thể. - Chúng ta có thể mở rộng enum hiện có mà không phá vỡ bất kỳ chức năng hiện có nào. Ví dụ, chúng ta có thể thêm một field NEW vào ThreadStates enum mà không ảnh hưởng đến bất kỳ chức năng hiện có nào.
- Vì các enum field là các constant (hằng số). Theo Java best practice, bạn nên viết chúng bằng chữ in hoa và gạch dưới cho dấu cách. Ví dụ: EAST, WEST, EAST_DIRECTION v.v.
- Các enum constant ngầm định là static và final.
- Các enum constant là final nhưng biến của nó vẫn có thể được thay đổi. Ví dụ, chúng ta có thể sử dụng method
setPriority()để thay đổi độ ưu tiên của các enum constant. Chúng ta sẽ thấy nó được sử dụng trong ví dụ dưới đây. - Vì các enum constant là final, chúng ta có thể so sánh chúng một cách an toàn bằng toán tử
==và các methodequals(). Cả hai sẽ cho cùng một kết quả.
Java EnumSet, EnumMap, valueOf()
Bây giờ chúng ta đã biết hầu hết các tính năng của Enum, hãy cùng xem xét chương trình ví dụ Java Enum. Sau đó, chúng ta sẽ tìm hiểu thêm một số tính năng của enum.
package com.journaldev.enums;
import java.io.IOException;
import java.util.EnumMap;
import java.util.EnumSet;
import java.util.Set;
public class JavaEnumExamples {
public static void main(String[] args) throws IOException {
usingEnumMethods();
usingEnumValueOf();
usingEnumValues();
usingEnumInSwitch(ThreadStatesEnum.START);
usingEnumInSwitch(ThreadStatesEnum.DEAD);
usingEnumMap();
usingEnumSet();
}
private static void usingEnumSet() {
EnumSet enumSet = EnumSet.allOf(ThreadStatesEnum.class);
for(ThreadStatesEnum tsenum : enumSet){
System.out.println("Using EnumSet, priority = "+tsenum.getPriority());
}
}
private static void usingEnumMap() {
EnumMap<ThreadStatesEnum, String> enumMap = new EnumMap<ThreadStatesEnum,String>(ThreadStatesEnum.class);
enumMap.put(ThreadStatesEnum.START, "Thread is started");
enumMap.put(ThreadStatesEnum.RUNNING, "Thread is running");
enumMap.put(ThreadStatesEnum.WAITING, "Thread is waiting");
enumMap.put(ThreadStatesEnum.DEAD, "Thread is dead");
Set keySet = enumMap.keySet();
for(ThreadStatesEnum key : keySet){
System.out.println("key="+key.toString()+":: value="+enumMap.get(key));
}
}
private static void usingEnumInSwitch(ThreadStatesEnum th) {
switch (th){
case START:
System.out.println("START thread");
break;
case WAITING:
System.out.println("WAITING thread");
break;
case RUNNING:
System.out.println("RUNNING thread");
break;
case DEAD:
System.out.println("DEAD thread");
}
}
private static void usingEnumValues() {
ThreadStatesEnum[] thArray = ThreadStatesEnum.values();
for(ThreadStatesEnum th : thArray){
System.out.println(th.toString() + "::priority="+th.getPriority());
}
}
private static void usingEnumValueOf() {
ThreadStatesEnum th = Enum.valueOf(ThreadStatesEnum.class, "START");
System.out.println("th priority="+th.getPriority());
}
private static void usingEnumMethods() throws IOException {
ThreadStatesEnum thc = ThreadStatesEnum.DEAD;
System.out.println("priority is:"+thc.getPriority());
thc = ThreadStatesEnum.DEAD;
System.out.println("Using overriden method."+thc.toString());
thc = ThreadStatesEnum.START;
System.out.println("Using overriden method."+thc.toString());
thc.setPriority(10);
System.out.println("Enum Constant variable changed priority value="+thc.getPriority());
thc.close();
}
}
Trước khi giải thích các tính năng quan trọng khác của enum, hãy cùng xem output của chương trình trên.
priority is:4
Using overriden method.Default ThreadStatesConstructors implementation. Priority=4
Using overriden method.START implementation. Priority=1
Enum Constant variable changed priority value=10
Close of Enum
th priority=10
START implementation. Priority=10::priority=10
Default ThreadStatesConstructors implementation. Priority=2::priority=2
Default ThreadStatesConstructors implementation. Priority=3::priority=3
Default ThreadStatesConstructors implementation. Priority=4::priority=4
START thread
DEAD thread
key=START:: value=Thread is started
key=RUNNING:: value=Thread is running
key=WAITING:: value=Thread is waiting
key=DEAD:: value=Thread is dead
Using EnumSet, priority = 10
Using EnumSet, priority = 2
Using EnumSet, priority = 3
Using EnumSet, priority = 4
Những điểm quan trọng
usingEnumMethods()minh họa cách tạo một enum object và cách chúng ta có thể sử dụng các method của nó. Nó cũng cho thấy cách sử dụng methodsetPriority(int i)để thay đổi biến của enum.usingEnumValueOf()hiển thị cách sử dụngjava.lang.Enum.valueOf(enumType, name)mà qua đó chúng ta có thể tạo một enum object từ String. Nó tạo raIllegalArgumentExceptionnếu kiểu enum được chỉ định không có hằng số với tên được chỉ định, hoặc class object được chỉ định không đại diện cho một kiểu enum. Nó cũng tạo raNullPointerExceptionnếu bất kỳ đối số nào là null.usingEnumValues()hiển thị cách sử dụng methodvalues()trả về một mảng chứa tất cả các giá trị của enum theo thứ tự chúng được khai báo. Lưu ý rằng method này được Java compiler tự động tạo cho mọi enum. Bạn sẽ không tìm thấy triển khaivalues()trong classjava.lang.Enum.usingEnumInSwitch()hiển thị cách sử dụng các enum constant trong câu lệnh switch.usingEnumMap()hiển thị cách sử dụngjava.util.EnumMap, được giới thiệu trong Java 1.5 Collections Framework. EnumMap là triển khai của Map được sử dụng với các key là kiểu enum. Tất cả các key trong một enum map phải đến từ một kiểu enum duy nhất được chỉ định, rõ ràng hoặc ngầm định, khi map được tạo. Chúng ta không thể sử dụngnulllàm key choEnumMapvàEnumMapkhông được đồng bộ hóa.usingEnumSet()hiển thị cách sử dụngjava.util.EnumSet, là triển khai của Set được sử dụng với các kiểu enum. Tất cả các phần tử trong mộtenum setphải đến từ một kiểu enum duy nhất được chỉ định, rõ ràng hoặc ngầm định, khi set được tạo. EnumSet không được đồng bộ hóa và các phần tử null không được phép. Nó cũng cung cấp một số method hữu ích nhưcopyOf(Collection<E> c),of(E first, E... rest)vàcomplementOf(EnumSet<E> s).