Trang chủHướng dẫnEnum trong Java: Khái niệm, cú pháp và ứng dụng thực tế
Java

Enum trong Java: Khái niệm, cú pháp và ứng dụng thực tế

CyStack blog 6 phút để đọc
CyStack blog22/07/2025
Locker Avatar

Bao Tran

Web Developer

Locker logo social
Reading Time: 6 minutes

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

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, EnumSetEnumMap 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.

  1. 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ào simpleEnumExample, do đó nó cung cấp type safety.
  2. Chúng ta có thể thay đổi giá trị của các int constants trong class ThreadStatesConstant như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

  1. Tất cả Java enum ngầm định mở rộng (implicitly extends) java.lang.Enum class, 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.
  2. enum là 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.enum không phải là một tên package hợp lệ.
  3. Enum có thể triển khai các interface. Như trong ví dụ enum trên, nó đang triển khai interface Closeable.
  4. Các constructor của Enum luôn là private.
  5. Chúng ta không thể tạo instance của enum bằng toán tử new.
  6. 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ó.
  7. 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ó.
  8. 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.
  9. 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ể.
  10. 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.
  11. 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.
  12. Các enum constant ngầm định là static và final.
  13. 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.
  14. 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 method equals(). 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

  1. 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 method setPriority(int i) để thay đổi biến của enum.
  2. usingEnumValueOf() hiển thị cách sử dụng java.lang.Enum.valueOf(enumType, name) mà qua đó chúng ta có thể tạo một enum object từ String. Nó tạo ra IllegalArgumentException nế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 ra NullPointerException nếu bất kỳ đối số nào là null.
  3. usingEnumValues() hiển thị cách sử dụng method values() 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 khai values() trong class java.lang.Enum.
  4. usingEnumInSwitch() hiển thị cách sử dụng các enum constant trong câu lệnh switch.
  5. usingEnumMap() hiển thị cách sử dụng java.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ụng null làm key cho EnumMapEnumMap không được đồng bộ hóa.
  6. usingEnumSet() hiển thị cách sử dụng java.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ột enum set phả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)complementOf(EnumSet<E> s).

0 Bình luận

Đăng nhập để thảo luận

Chuyên mục Hướng dẫn

Tổng hợp các bài viết hướng dẫn, nghiên cứu và phân tích chi tiết về kỹ thuật, các xu hướng công nghệ mới nhất dành cho lập trình viên.

Đăng ký nhận bản tin của chúng tôi

Hãy trở thành người nhận được các nội dung hữu ích của CyStack sớm nhất

Xem chính sách của chúng tôi Chính sách bảo mật.

Đăng ký nhận Newsletter

Nhận các nội dung hữu ích mới nhất