Enumがインターフェイスを実装するのはなぜですか?


回答:


130

列挙型は、パッシブセット(色など)を表す必要はありません。彼らは、機能性と、より複雑なオブジェクトを表すことができ、そのため、あなたは、おそらくこれらにさらに機能を追加したいです-あなたのようなインターフェースがあり、たとえばPrintableReportableこれらをサポートするなどして部品を。


259

これが1つの例です(同様の/より良いものは、Effective 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 :-/
}

次に、単純演算子と複合演算子の両方のリストを取得します。

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

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

したがって、ここではインターフェイスを使用して、拡張可能な列挙型をシミュレートします(インターフェイスを使用しないと実現できません)。


3
複雑な演算子は "pow"などです。
Pimgd

2
そのEnum構文(メンバーの後ろに中かっこが付いている)を初めて目にし、Javaで6年間プログラミングしています。TIL。
jrahhali

23

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
}

2
はい。変!EnumはすでにComparableインターフェースを実装しており、オーバーライドを許可していません。enumクラスのJavaソースコードを検索したところ、compareTo実装が見つかりませんでした。ENUMのcompareToメソッド内で何が比較されるかを誰かに教えてもらえますか?
AKh 2012

2
@AKh序数を比較します。つまり、自然順序付けは、列挙型定数がソースファイルにある順序です。
2012

列挙型変数は最終的なものなので、比較は==で発生しますよね?
djangofan

@djangofanはい。
ジョーン

17

よく使うケースがあります。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);

13

Enumはインターフェイスを実装できるため、シングルトンパターンの厳密な適用に使用できます。標準クラスをシングルトンにしようとすると...

  • リフレクション技術を使用してプライベートメソッドをパブリックとして公開する可能性
  • シングルトンから継承し、シングルトンのメソッドを他のものでオーバーライドするため

シングルトンとしての列挙型は、これらのセキュリティ問題の防止に役立ちます。これは、Enumsをクラスとして機能させ、インターフェースを実装させる理由の1つであった可能性があります。ただの推測です。

詳細については、javaの/programming/427902/java-enum-singletonおよびSingletonクラスを参照してください


1
for inheriting from your singleton and overriding your singleton's methods with something else。あなたはそれfinal classを防ぐために単に使うことができます
Dylanthepiguy

10

拡張性のために必要です。開発したAPIを誰かが使用する場合、定義する列挙型は静的です。追加または変更することはできません。ただし、インターフェースの実装を許可すると、APIを使用する人は同じインターフェースを使用して独自の列挙型を開発できます。次に、この列挙型を、標準列挙型と一緒に列挙型をまとめる列挙型マネージャーに登録できます。

編集:@Helperメソッドはこれの完璧な例を持っています。他のライブラリーに新しいオペレーターを定義させ、マネージャー・クラスに「ねえ、この列挙型は存在する-登録する」と伝えることを考えてください。それ以外の場合は、独自のコードでのみ演算子を定義できます-拡張性はありません。


7

列挙型は変装したクラスにすぎないため、ほとんどの場合、列挙型を使用してクラスで実行できることは何でも実行できます。

enumがインターフェイスを実装できない理由を考えることはできませんが、同時に、それらを行う理由も考えられません。

インターフェースやメソッドなどを列挙型に追加し始めたら、それをクラスにすることを検討する必要があります。もちろん、非伝統的な列挙型のことを行う正当なケースがあると私は確信しています。制限は人工的なものであるため、そこで人々が望むことをできるようにしたいと思います。


4
「...つまり、ほとんどの場合、列挙型で実行できるクラスで実行できることは何でも」、しかし、列挙型に抽象クラスから継承させることはできません。ええ、「大部分」に重点を置いています。
アダムパーキン2013

5
これは、列挙型はすべてjava.lang.Enumを拡張し、Javaクラスは1つのクラスからしか拡張できないためです。
TofuBeer 2013

6

たとえば、Logger列挙型があるとします。次に、インターフェイスにdebug、info、warning、errorなどのロガーメソッドが必要です。それはあなたのコードを疎結合にします。



5

enumをインターフェイスで使用するのに最適な使用例の1つは、述語フィルターです。これは、Apacheコレクションのタイプの欠如を修正する非常にエレガントな方法です(他のライブラリが使用されていない場合)。

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

戦略について述べた上記の投稿では、enumsを使用した戦略パターンの軽量で優れた実装によって、どのような効果が得られるかを十分に強調していませんでした。

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

戦略ごとに個別のコンパイルユニットを必要とせずに、すべての戦略を1か所にまとめることができます。あなただけで素晴らしい動的ディスパッチを取得します:

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

緩やかに型付けされた素晴らしい言語のようにJavaを読み取らせます


3

列挙型はJavaクラスのようなもので、コンストラクター、メソッドなどを持つことができます。列挙型で実行できないのは、だけですnew EnumName()。インスタンスは、列挙型宣言で事前定義されています。


どうenum Foo extends SomeOtherClassですか?したがって、通常のクラスとまったく同じではなく、実際にはかなり異なります。
アダムパーキン2013

@Adam:Enumがクラスに似ている点は、クラスと異なる点よりもはるかに多いと思います。しかし、いいえ、それらは同一ではありません。
scottb 2013

列挙型を逆コンパイルすると、列挙型を拡張するクラスであり、コンストラクタ/初期化フィールドで「定数」がすでにインスタンス化されていることがわかります。
Cosmin Cosmin 2013

3

別の可能性:

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

そして使用:

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

3

jarファイルに定数を作成する場合、ユーザーが列挙値を拡張できるようにすると便利な場合があります。PropertyFileキーに列挙型を使用しましたが、誰も新しいものを追加できなかったために行き詰まりました!以下ははるかにうまく機能します。

与えられた:

public interface Color {
  String fetchName();
}

そして:

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

jarに1つの列挙型を含めることができます。

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

ユーザーはそれを拡張して自分の色を追加することができます。

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

2

ここに私の理由があります...

Enumの値をJavaFX ComboBoxに入力しました。Identifiable(1つのメソッドを指定する:識別)のインターフェイスを持っています。これにより、オブジェクトが検索目的でアプリケーションに対して自分自身を識別する方法を指定できます。このインターフェイスを使用すると、任意のタイプのオブジェクト(オブジェクトがIDに使用できるフィールド)のリストをスキャンして、IDの一致を確認できます。

ComboBoxリストでID値の一致を見つけたいのですが。Enum値を含むComboBoxでこの機能を使用するには、EnumにIdentifiableインターフェースを実装できる必要があります(これは、Enumの場合は簡単に実装できます)。


1

そこからインスタンス制御(各戦略はシングルトン)を維持するための戦略を記述するインターフェイスで内部列挙型を使用しました。

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

enumが戦略を実装するという事実は、enumクラスがその囲まれたインスタンスのプロキシとして機能できるようにするのに便利です。インターフェースも実装しています。

それは一種のStrategyEnumProxyです:Pクライアントコードは次のようになります:

VectorizeStrategy.ConcreteStrategy.DEFAULT.processImage(img);

それがインターフェイスを実装していなかった場合、それはそうでした:

VectorizeStrategy.ConcreteStrategy.DEFAULT.getInstance().processImage(img);
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.