Programing

Enum이 인터페이스를 구현하는 이유는 무엇입니까?

crosscheck 2020. 5. 20. 21:36
반응형

Enum이 인터페이스를 구현하는 이유는 무엇입니까?


방금 Java가 열거 형이 인터페이스를 구현하도록 허용한다는 것을 알았습니다. 이를위한 좋은 사용 사례는 무엇입니까?


열거 형은 패시브 세트 (예 : 색상)를 나타내지 않아도됩니다. 그들은 기능을 더 복잡한 객체를 나타낼 수 있습니다, 그래서 당신은 가능성이에 더 기능을 추가 할 수있는 좋은 방법입니다 - 당신과 같은 인터페이스 할 수 있습니다 예 Printable, Reportable이들을 지원 등 및 구성 요소를.


다음은 한 가지 예입니다 (유효한 Java 2nd Edition에있는 유사 / 더 나은 예).

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 :-/
}

이제 Simple + Complex 연산자의 목록을 얻으려면 다음을 수행하십시오.

List<Operator> operators = new ArrayList<Operator>();

operators.addAll(Arrays.asList(SimpleOperators.values()));
operators.addAll(Arrays.asList(ComplexOperators.values()));

따라서 인터페이스를 사용하여 확장 가능한 열거 형을 시뮬레이션합니다 (인터페이스를 사용하지 않으면 불가능합니다).


Comparable여러 사람에 의해 주어진 예제는 여기에 있기 때문에, 잘못 Enum이미 구현이. 당신은 그것을 무시할 수 없습니다.

더 좋은 예는 데이터 유형을 정의하는 인터페이스를 갖는 것입니다. 간단한 유형을 구현하는 열거 형을 가질 수 있고 복잡한 유형을 구현하는 일반 클래스를 가질 수 있습니다.

interface DataType {
  // methods here
}

enum SimpleDataType implements DataType {
  INTEGER, STRING;

  // implement methods
}

class IdentifierDataType implements DataType {
  // implement interface and maybe add more specific methods
}

내가 자주 사용하는 경우가 있습니다. IdUtil매우 간단한 Identifiable인터페이스를 구현하는 객체로 작업하는 정적 메서드 가있는 클래스가 있습니다.

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

내가 만들면 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;
        }

    }

그런 다음 id이 방법으로 얻을 수 있습니다 .

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

Enum은 인터페이스를 구현할 수 있으므로 싱글 톤 패턴을 엄격하게 적용하는 데 사용할 수 있습니다. 싱글 톤으로 표준 클래스를 만들려고하면 ...

  • 리플렉션 기법을 사용하여 개인 메서드를 공개로 노출 할 수있는 가능성
  • 싱글 톤에서 상속하고 싱글 톤의 메소드를 다른 것으로 재정의

싱글 톤으로서 열거 형은 이러한 보안 문제를 방지하는 데 도움이됩니다. 이것은 Enum이 클래스 역할을하고 인터페이스를 구현하게하는 주된 이유 중 하나 일 수 있습니다. 그냥 추측.

자세한 내용은 https://stackoverflow.com/questions/427902/java-enum-singletonJava의 싱글 톤 클래스를 참조 하십시오 .


확장 성이 필요합니다. 누군가 개발 한 API를 사용하는 경우 정의한 열거 형은 정적입니다. 추가하거나 수정할 수 없습니다. 그러나 인터페이스를 구현하게하면 API를 사용하는 사람이 동일한 인터페이스를 사용하여 자체 열거 형을 개발할 수 있습니다. 그런 다음이 열거 형을 열거 형 관리자에 등록하여 표준 인터페이스와 함께 열거 형을 집약합니다.

편집 : @Helper Method에는 완벽한 예가 있습니다. 다른 라이브러리가 새로운 연산자를 정의하고 관리자 클래스에 '이 열거 형이 존재합니다-등록하십시오'라고 알려주십시오. 그렇지 않으면 자신의 코드에서만 연산자를 정의 할 수 있습니다. 확장 성이 없습니다.


예를 들어 로거 열거 형이있는 경우 그런 다음 인터페이스에 디버그, 정보, 경고 및 오류와 같은 로거 메소드가 있어야합니다. 코드가 느슨하게 결합됩니다.


열거 형은 변장 된 클래스 일 뿐이므로 대부분 열거 형으로 수행 할 수있는 클래스로 수행 할 수있는 모든 작업입니다.

열거 형이 인터페이스를 구현할 수 없어야하는 이유를 생각할 수 없으며 동시에 그들에게도 좋은 이유를 생각할 수 없습니다.

인터페이스 또는 메소드와 같은 것을 열거 형에 추가하기 시작하면 실제로 클래스로 만드는 것을 고려해야합니다. 물론 나는 비 전통적인 열거 형 일을하는 데 유효한 사례가 있다고 확신하며, 그 한계는 인공적인 것이기 때문에 사람들이 원하는 것을 할 수있게하는 것을 선호합니다.


이것에 대한 가장 일반적인 사용법은 두 열거 형의 값을 하나의 그룹으로 병합하고 유사하게 취급하는 것입니다. 예를 들어 Fruits and Vegatables 가입 방법을 참조하십시오 .


열거 형을 인터페이스와 함께 사용하는 가장 좋은 사용 사례 중 하나는 Predicate 필터입니다. 아파치 컬렉션의 typness 부족을 해결하는 매우 우아한 방법입니다 (다른 라이브러리를 사용할 수없는 경우).

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

Enums are like Java Classes, they can have Constructors, Methods, etc. The only thing that you can't do with them is new EnumName(). The instances are predefined in your enum declaration.


Here's my reason why ...

I have populated a JavaFX ComboBox with the values of an Enum. I have an interface, Identifiable (specifying one method: identify), that allows me to specify how any object identifies itself to my application for searching purposes. This interface enables me to scan lists of any type of objects (whichever field the object may use for identity) for an identity match.

I'd like to find a match for an identity value in my ComboBox list. In order to use this capability on my ComboBox containing the Enum values, I must be able to implement the Identifiable interface in my Enum (which, as it happens, is trivial to implement in the case of an Enum).


Another posibility:

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

and using:

Predicate<Number> p = IS_NOT_NULL.and(IS_NOT_AN_INTEGER).and(IS_POSITIVE);

The post above that mentioned strategies didn't touch enough on just what a nice lightweight implementation of the strategy pattern using enums gets you:

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

You can have all your strategies in one place without needing a separate compilation unit for each. You get a nice dynamic dispatch just with:

Strategy.valueOf("A").execute();

Makes java read almost like a nice loosely typed language!


I used an inner enum in an interface describing a strategy to keep instance control (each strategy is a Singleton) from there.

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

The fact that the enum implements the strategy is convenient to allow the enum class to act as proxy for its enclosed Instance. which also implements the interface.

it's a sort of strategyEnumProxy :P the clent code looks like this:

VectorizeStrategy.ConcreteStrategy.DEFAULT.processImage(img);

If it didn't implement the interface it'd had been:

VectorizeStrategy.ConcreteStrategy.DEFAULT.getInstance().processImage(img);

When creating constants in a jar file, it is often helpful to let users extend enum values. Below is a simple example, but we use enums for PropertyFile keys and got stuck because nobody could add any new ones! Below would have worked much better.

Given:

public interface Color {
  String fetchName();
}

and:

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

one could have one enum in the jar:

public enum Colors implements Color {
  BLUE, RED, GREEN;
  @Override
  public String fetchName() {
    return this.name();
  }
}

and a user could extend it to add his own colors:

public enum MyColors implements Color {
  BROWN, GREEN, YELLOW;
  @Override
  public String fetchName() {
    return this.name();
  }
}

참고URL : https://stackoverflow.com/questions/2709593/why-would-an-enum-implement-an-interface

반응형