Tại sao Enum thực hiện Giao diện?


Câu trả lời:


130

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, Reportablev.v. và các thành phần hỗ trợ các giao diện này.


259

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).


3
Toán tử phức tạp có thể là "pow" và tương tự.
Pimgd

2
Lần đầu tiên nhìn thấy cú pháp Enum (với các dấu ngoặc nhọn sau các thành viên) và tôi đã lập trình với Java được 6 năm. GẠCH
jrahhali

23

Các Comparableví 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
}

2
Vâng, lạ !! Enum đã thực hiện giao diện so sánh và không cho phép ghi đè. Tôi đã tra cứu mã nguồn java cho lớp enum và không thể tìm thấy việc thực hiện so sánh. Bất cứ ai cũng có thể cho tôi biết những gì được so sánh bên trong một phương thức so sánh với ENUM không?
AKh

2
@AKh nó so sánh các thứ tự, có nghĩa là thứ tự tự nhiên là thứ tự các hằng số enum có trong tệp nguồn.
Jorn

Enum vars là cuối cùng và do đó so sánh xảy ra với ==, phải không?
djangofan

@djangofan vâng.
Jorn

17

Có một trường hợp tôi thường sử dụng. Tôi có một IdUtillớ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 Identifiablegiao 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 idnày:

MyEnum e = IdUtil.get(MyEnum.class, 1);

13

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

  • cho khả năng sử dụng các kỹ thuật phản chiếu để phơi bày các phương thức riêng tư là công khai
  • để kế thừa từ singleton của bạn và ghi đè các phương thức của singleton của bạn bằng một cái gì đó khác

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-singletonlớp Singleton trong java để biết thêm thảo luận.


1
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 đó
Dylanthepiguy

10

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.


7

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 ở đó.


4
"... Vì vậy, đối với hầu hết mọi thứ bạn có thể làm với một lớp bạn có thể làm với một enum", nhưng tôi không thể nói rằng enum của tôi được thừa hưởng từ một lớp trừu tượng. Vì vậy, yeah, nhấn mạnh vào "phần lớn".
Adam Parkin

5
Đó là bởi vì tất cả các enums đều mở rộng java.lang.Enum và các lớp Java chỉ có thể mở rộng từ một lớp.
TofuBeer

6

Ví dụ nếu bạn có một enger Logger. Sau đó, bạn nên có các phương thức logger như gỡ lỗi, thông tin, cảnh báo và lỗi trong giao diện. Nó làm cho mã của bạn được ghép lỏng lẻo.


6

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 .


5

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);
    }
}

5

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!


3

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.


Thế cò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.
Adam Parkin

@Adam: Tôi sẽ nói rằng những cách mà Enums giống với các lớp nhiều hơn rất nhiều so với những cách mà chúng khác với các lớp. Nhưng, không, chúng không giống nhau.
scottb

nếu bạn dịch ngược một enum, bạn sẽ thấy đó là một lớp mở rộng liệt kê và có "hằng số" đã được khởi tạo trong các trường khởi tạo / khởi tạo.
Cosmin Cosmin

3

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);

3

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();
  }
}

2

Đâ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).


1

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);
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.