Câu trả lời:
Enums không phải đại diện cho các bộ thụ động (ví dụ: màu sắc). Chúng có thể đại diện cho các đối tượng phức tạp hơn với chức năng và do đó bạn có thể muốn thêm chức năng này vào các đối tượng này - ví dụ: bạn có thể có các giao diện như Printable
, Reportable
v.v. và các thành phần hỗ trợ các giao diện này.
Dưới đây là một ví dụ (một ví dụ tương tự / tốt hơn được tìm thấy trong Phiên bản Java hiệu quả thứ 2):
public interface Operator {
int apply (int a, int b);
}
public enum SimpleOperators implements Operator {
PLUS {
int apply(int a, int b) { return a + b; }
},
MINUS {
int apply(int a, int b) { return a - b; }
};
}
public enum ComplexOperators implements Operator {
// can't think of an example right now :-/
}
Bây giờ để có danh sách cả hai Toán tử đơn giản + phức tạp:
List<Operator> operators = new ArrayList<Operator>();
operators.addAll(Arrays.asList(SimpleOperators.values()));
operators.addAll(Arrays.asList(ComplexOperators.values()));
Vì vậy, ở đây bạn sử dụng một giao diện để mô phỏng các enum có thể mở rộng (điều này sẽ không thể thực hiện được nếu không sử dụng giao diện).
Các Comparable
ví dụ được đưa ra bởi một số người ở đây là sai, vìEnum
đã cụ đó. Bạn thậm chí không thể ghi đè lên nó.
Một ví dụ tốt hơn là có một giao diện xác định, giả sử, một kiểu dữ liệu. Bạn có thể có một enum để thực hiện các kiểu đơn giản và có các lớp bình thường để thực hiện các kiểu phức tạp:
interface DataType {
// methods here
}
enum SimpleDataType implements DataType {
INTEGER, STRING;
// implement methods
}
class IdentifierDataType implements DataType {
// implement interface and maybe add more specific methods
}
Có một trường hợp tôi thường sử dụng. Tôi có một IdUtil
lớp với các phương thức tĩnh để làm việc với các đối tượng thực hiện Identifiable
giao diện rất đơn giản :
public interface Identifiable<K> {
K getId();
}
public abstract class IdUtil {
public static <T extends Enum<T> & Identifiable<S>, S> T get(Class<T> type, S id) {
for (T t : type.getEnumConstants()) {
if (Util.equals(t.getId(), id)) {
return t;
}
}
return null;
}
public static <T extends Enum<T> & Identifiable<S>, S extends Comparable<? super S>> List<T> getLower(T en) {
List<T> list = new ArrayList<>();
for (T t : en.getDeclaringClass().getEnumConstants()) {
if (t.getId().compareTo(en.getId()) < 0) {
list.add(t);
}
}
return list;
}
}
Nếu tôi tạo một Identifiable
enum
:
public enum MyEnum implements Identifiable<Integer> {
FIRST(1), SECOND(2);
private int id;
private MyEnum(int id) {
this.id = id;
}
public Integer getId() {
return id;
}
}
Sau đó, tôi có thể lấy nó bằng cách id
này:
MyEnum e = IdUtil.get(MyEnum.class, 1);
Vì Enums có thể thực hiện các giao diện, chúng có thể được sử dụng để thực thi nghiêm ngặt mẫu singleton. Cố gắng tạo một lớp tiêu chuẩn cho một người độc thân cho phép ...
Enums như singletons giúp ngăn chặn các vấn đề bảo mật. Đây có thể là một trong những lý do góp phần để Enums hoạt động như các lớp và thực hiện các giao diện. Chỉ là một phỏng đoán.
Xem /programming/427902/java-enum-singleton và lớp Singleton trong java để biết thêm thảo luận.
for inheriting from your singleton and overriding your singleton's methods with something else
. bạn chỉ có thể sử dụng một final class
để ngăn chặn điều đó
Nó được yêu cầu cho khả năng mở rộng - nếu ai đó sử dụng API bạn đã phát triển, thì các enum bạn xác định là tĩnh; chúng không thể được thêm vào hoặc sửa đổi. Tuy nhiên, nếu bạn để nó triển khai một giao diện, người sử dụng API có thể phát triển enum của riêng họ bằng cùng một giao diện. Sau đó, bạn có thể đăng ký enum này với một trình quản lý enum kết hợp các enum cùng với giao diện chuẩn.
Chỉnh sửa: Phương thức @Helper có ví dụ hoàn hảo về điều này. Hãy suy nghĩ về việc có các thư viện khác xác định các toán tử mới và sau đó nói với lớp người quản lý rằng 'này, enum này tồn tại - hãy đăng ký nó'. Mặt khác, bạn chỉ có thể xác định Toán tử trong mã của riêng bạn - sẽ không có khả năng mở rộng.
Enums chỉ là các lớp được ngụy trang, vì vậy, đối với hầu hết các phần, bất cứ điều gì bạn có thể làm với một lớp bạn có thể làm với một enum.
Tôi không thể nghĩ ra một lý do mà một enum không thể thực hiện giao diện, đồng thời tôi cũng không thể nghĩ ra một lý do chính đáng nào cho chúng.
Tôi sẽ nói một khi bạn bắt đầu thêm những thứ như giao diện, hoặc phương thức vào một enum, bạn thực sự nên xem xét việc biến nó thành một lớp thay thế. Tất nhiên tôi chắc chắn có những trường hợp hợp lệ để làm những việc không phải là truyền thống, và vì giới hạn sẽ là giả tạo, tôi ủng hộ việc để mọi người làm những gì họ muốn ở đó.
Cách sử dụng phổ biến nhất cho việc này sẽ là hợp nhất các giá trị của hai enum thành một nhóm và đối xử với chúng tương tự nhau. Ví dụ, xem cách tham gia Trái cây và Ăn chay .
Một trong những trường hợp sử dụng tốt nhất để tôi sử dụng enum với giao diện là bộ lọc Vị ngữ. Đó là cách rất thanh lịch để khắc phục sự thiếu chính tả của các bộ sưu tập apache (Nếu các thư viện khác có thể không được sử dụng).
import java.util.ArrayList;
import java.util.Collection;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.Predicate;
public class Test {
public final static String DEFAULT_COMPONENT = "Default";
enum FilterTest implements Predicate {
Active(false) {
@Override
boolean eval(Test test) {
return test.active;
}
},
DefaultComponent(true) {
@Override
boolean eval(Test test) {
return DEFAULT_COMPONENT.equals(test.component);
}
}
;
private boolean defaultValue;
private FilterTest(boolean defautValue) {
this.defaultValue = defautValue;
}
abstract boolean eval(Test test);
public boolean evaluate(Object o) {
if (o instanceof Test) {
return eval((Test)o);
}
return defaultValue;
}
}
private boolean active = true;
private String component = DEFAULT_COMPONENT;
public static void main(String[] args) {
Collection<Test> tests = new ArrayList<Test>();
tests.add(new Test());
CollectionUtils.filter(tests, FilterTest.Active);
}
}
Bài đăng ở trên đã đề cập đến các chiến lược không đủ nhấn mạnh việc triển khai mô hình chiến lược nhẹ nhàng bằng cách sử dụng enums giúp bạn:
public enum Strategy {
A {
@Override
void execute() {
System.out.print("Executing strategy A");
}
},
B {
@Override
void execute() {
System.out.print("Executing strategy B");
}
};
abstract void execute();
}
Bạn có thể có tất cả các chiến lược của mình ở một nơi mà không cần một đơn vị biên dịch riêng cho mỗi nơi. Bạn nhận được một công văn năng động tốt đẹp chỉ với:
Strategy.valueOf("A").execute();
Làm cho java đọc gần giống như một ngôn ngữ đánh máy lỏng lẻo!
Enums giống như các lớp Java, chúng có thể có Trình xây dựng, Phương thức, v.v. Điều duy nhất bạn không thể làm với chúng là new EnumName()
. Các trường hợp được xác định trước trong khai báo enum của bạn.
enum Foo extends SomeOtherClass
? Vì vậy, không hoàn toàn giống như một lớp học thông thường, trên thực tế, hoàn toàn khác.
Một khả năng khác:
public enum ConditionsToBeSatisfied implements Predicate<Number> {
IS_NOT_NULL(Objects::nonNull, "Item is null"),
IS_NOT_AN_INTEGER(item -> item instanceof Integer, "Item is not an integer"),
IS_POSITIVE(item -> item instanceof Integer && (Integer) item > 0, "Item is negative");
private final Predicate<Number> predicate;
private final String notSatisfiedLogMessage;
ConditionsToBeSatisfied(final Predicate<Number> predicate, final String notSatisfiedLogMessage) {
this.predicate = predicate;
this.notSatisfiedLogMessage = notSatisfiedLogMessage;
}
@Override
public boolean test(final Number item) {
final boolean isNotValid = predicate.negate().test(item);
if (isNotValid) {
log.warn("Invalid {}. Cause: {}", item, notSatisfiedLogMessage);
}
return predicate.test(item);
}
}
và sử dụng:
Predicate<Number> p = IS_NOT_NULL.and(IS_NOT_AN_INTEGER).and(IS_POSITIVE);
Khi tạo các hằng số trong tệp jar, thường hữu ích khi cho phép người dùng mở rộng giá trị enum. Chúng tôi đã sử dụng enum cho các khóa PropertyFile và bị kẹt vì không ai có thể thêm bất kỳ khóa mới nào! Dưới đây sẽ làm việc tốt hơn nhiều.
Được:
public interface Color {
String fetchName();
}
và:
public class MarkTest {
public static void main(String[] args) {
MarkTest.showColor(Colors.BLUE);
MarkTest.showColor(MyColors.BROWN);
}
private static void showColor(Color c) {
System.out.println(c.fetchName());
}
}
người ta có thể có một enum trong bình:
public enum Colors implements Color {
BLUE, RED, GREEN;
@Override
public String fetchName() {
return this.name();
}
}
và người dùng có thể mở rộng nó để thêm màu sắc của riêng mình:
public enum MyColors implements Color {
BROWN, GREEN, YELLOW;
@Override
public String fetchName() {
return this.name();
}
}
Đây là lý do của tôi tại sao ...
Tôi đã tạo ra một ComboBox JavaFX với các giá trị của Enum. Tôi có một giao diện, Nhận dạng (chỉ định một phương thức: xác định), cho phép tôi chỉ định cách bất kỳ đối tượng nào nhận dạng chính nó vào ứng dụng của tôi cho mục đích tìm kiếm. Giao diện này cho phép tôi quét danh sách của bất kỳ loại đối tượng nào (bất kỳ trường nào mà đối tượng có thể sử dụng để nhận dạng) cho phù hợp với danh tính.
Tôi muốn tìm một kết quả khớp cho một giá trị nhận dạng trong danh sách ComboBox của tôi. Để sử dụng khả năng này trên ComboBox của tôi có chứa các giá trị Enum, tôi phải có khả năng triển khai giao diện Nhận dạng trong Enum của mình (điều này, như nó xảy ra, là rất nhỏ để thực hiện trong trường hợp của Enum).
Tôi đã sử dụng một enum bên trong trong một giao diện mô tả một chiến lược để giữ quyền kiểm soát cá thể (mỗi chiến lược là một Singleton) từ đó.
public interface VectorizeStrategy {
/**
* Keep instance control from here.
*
* Concrete classes constructors should be package private.
*/
enum ConcreteStrategy implements VectorizeStrategy {
DEFAULT (new VectorizeImpl());
private final VectorizeStrategy INSTANCE;
ConcreteStrategy(VectorizeStrategy concreteStrategy) {
INSTANCE = concreteStrategy;
}
@Override
public VectorImageGridIntersections processImage(MarvinImage img) {
return INSTANCE.processImage(img);
}
}
/**
* Should perform edge Detection in order to have lines, that can be vectorized.
*
* @param img An Image suitable for edge detection.
*
* @return the VectorImageGridIntersections representing img's vectors
* intersections with the grids.
*/
VectorImageGridIntersections processImage(MarvinImage img);
}
Thực tế là enum thực hiện chiến lược này thuận tiện cho phép lớp enum đóng vai trò ủy quyền cho Trường hợp kèm theo của nó. mà cũng thực hiện giao diện.
đó là một loại chiến lượcEnumProxy: P mã clent trông như thế này:
VectorizeStrategy.ConcreteStrategy.DEFAULT.processImage(img);
Nếu nó không thực hiện giao diện thì đó là:
VectorizeStrategy.ConcreteStrategy.DEFAULT.getInstance().processImage(img);