Tại sao sử dụng bất kỳ tính năng ngôn ngữ lập trình? Lý do chúng tôi có ngôn ngữ là vì
- Các lập trình viên để thể hiện các thuật toán một cách hiệu quả và chính xác trong một dạng máy tính có thể sử dụng.
- Các nhà bảo trì để hiểu các thuật toán mà người khác đã viết và thực hiện các thay đổi chính xác .
Enums cải thiện cả khả năng chính xác và dễ đọc mà không cần viết nhiều bản tóm tắt. Nếu bạn sẵn sàng viết bản soạn sẵn, thì bạn có thể "mô phỏng" enums:
public class Color {
private Color() {} // Prevent others from making colors.
public static final Color RED = new Color();
public static final Color AMBER = new Color();
public static final Color GREEN = new Color();
}
Bây giờ bạn có thể viết:
Color trafficLightColor = Color.RED;
Các nồi hơi ở trên có nhiều tác dụng tương tự như
public enum Color { RED, AMBER, GREEN };
Cả hai đều cung cấp cùng một mức độ kiểm tra trợ giúp từ trình biên dịch. Nồi hơi chỉ là gõ nhiều hơn. Nhưng tiết kiệm rất nhiều thao tác gõ giúp lập trình viên hiệu quả hơn (xem 1), vì vậy đây là một tính năng đáng giá.
Nó cũng đáng giá cho ít nhất một lý do nữa:
Chuyển báo cáo
Một điều mà static final
mô phỏng enum ở trên không cung cấp cho bạn là switch
trường hợp tốt đẹp . Đối với các loại enum, trình chuyển đổi Java sử dụng loại biến của nó để suy ra phạm vi của các trường hợp enum, vì vậy đối với các trường hợp enum Color
trên, bạn chỉ cần nói:
Color color = ... ;
switch (color) {
case RED:
...
break;
}
Lưu ý rằng nó không có Color.RED
trong các trường hợp. Nếu bạn không sử dụng enum, cách duy nhất để sử dụng số lượng được đặt tên switch
là:
public Class Color {
public static final int RED = 0;
public static final int AMBER = 1;
public static final int GREEN = 2;
}
Nhưng bây giờ một biến để giữ một màu phải có loại int
. Trình biên dịch đẹp kiểm tra enum và static final
mô phỏng đã biến mất. Không vui.
Một thỏa hiệp là sử dụng một thành viên có giá trị vô hướng trong mô phỏng:
public class Color {
public static final int RED_TAG = 1;
public static final int AMBER_TAG = 2;
public static final int GREEN_TAG = 3;
public final int tag;
private Color(int tag) { this.tag = tag; }
public static final Color RED = new Color(RED_TAG);
public static final Color AMBER = new Color(AMBER_TAG);
public static final Color GREEN = new Color(GREEN_TAG);
}
Hiện nay:
Color color = ... ;
switch (color.tag) {
case Color.RED_TAG:
...
break;
}
Nhưng lưu ý, thậm chí nhiều hơn nồi hơi!
Sử dụng enum như một singleton
Từ bản tóm tắt ở trên, bạn có thể thấy tại sao một enum cung cấp một cách để thực hiện một singleton. Thay vì viết:
public class SingletonClass {
public static final void INSTANCE = new SingletonClass();
private SingletonClass() {}
// all the methods and instance data for the class here
}
và sau đó truy cập nó với
SingletonClass.INSTANCE
chúng ta chỉ có thể nói
public enum SingletonClass {
INSTANCE;
// all the methods and instance data for the class here
}
Điều này cho chúng ta điều tương tự. Chúng ta có thể thoát khỏi điều này bởi vì các enum Java được triển khai dưới dạng các lớp đầy đủ chỉ với một ít đường cú pháp được rắc lên trên cùng. Đây là một lần nữa ít nồi hơi, nhưng nó không rõ ràng trừ khi thành ngữ quen thuộc với bạn. Tôi cũng không thích thực tế rằng bạn sẽ có được các chức năng enum khác nhau mặc dù họ không có ý nghĩa nhiều đối với singleton: ord
và values
, vv (Có thực sự là một mô phỏng phức tạp hơn nơi Color extends Integer
mà sẽ làm việc với switch, nhưng nó quá phức tạp mà nó thậm chí nhiều hơn cho thấy rõ tại saoenum
là một ý tưởng tốt hơn.)
Chủ đề an toàn
An toàn chủ đề là một vấn đề tiềm năng chỉ khi các singletons được tạo ra một cách lười biếng mà không có khóa.
public class SingletonClass {
private static SingletonClass INSTANCE;
private SingletonClass() {}
public SingletonClass getInstance() {
if (INSTANCE == null) INSTANCE = new SingletonClass();
return INSTANCE;
}
// all the methods and instance data for the class here
}
Nếu nhiều luồng gọi getInstance
đồng thời trong khi INSTANCE
vẫn là null, bất kỳ số lượng phiên bản nào cũng có thể được tạo. Thật tệ. Giải pháp duy nhất là thêm synchronized
quyền truy cập để bảo vệ biến INSTANCE
.
Tuy nhiên, static final
đoạn mã trên không có vấn đề này. Nó tạo ra cá thể háo hức vào thời gian tải lớp. Tải lớp được đồng bộ hóa.
Các enum
singleton rất lười biếng vì nó không được khởi tạo cho đến khi sử dụng lần đầu tiên. Khởi tạo Java cũng được đồng bộ hóa, vì vậy nhiều luồng không thể khởi tạo nhiều hơn một thể hiện của INSTANCE
. Bạn đang nhận được một singleton khởi tạo lười biếng với rất ít mã. Điểm trừ duy nhất là cú pháp khá tối nghĩa. Bạn cần biết thành ngữ hoặc hiểu kỹ về cách hoạt động của lớp tải và khởi tạo để biết điều gì đang xảy ra.