Javaにより列挙型がインターフェースを実装できることがわかりました。そのための良いユースケースは何でしょうか?
列挙型は、受動的なセット(色など)を表すだけではありません。機能を備えたより複雑なオブジェクトを表すことができるため、これらにさらに機能を追加する必要があります。 Printable
、Reportable
などのインターフェイスと、これらをサポートするコンポーネントがある場合があります。
次に例を示します(同様の/より良い例は、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()));
そのため、ここでは、インターフェイスを使用して拡張可能な列挙型をシミュレートします(インターフェイスを使用しないと不可能です)。
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がクラスとして機能し、インターフェイスを実装できるようにする大きな理由の1つであった可能性があります。ただの推測。
詳細については、 https://stackoverflow.com/questions/427902/Java-enum-singleton および Javaのシングルトンクラス を参照してください。
拡張性のために必要です。誰かが開発したAPIを使用する場合、定義する列挙型は静的です。追加または変更することはできません。ただし、インターフェイスを実装できるようにすると、APIを使用する人は同じインターフェイスを使用して独自の列挙を開発できます。次に、この列挙型を列挙マネージャーに登録して、列挙を標準インターフェースと組み合わせます。
編集:@Helperメソッドには、この完璧な例があります。他のライブラリで新しい演算子を定義し、マネージャークラスに「この列挙型が存在する-登録する」と伝えることを検討してください。それ以外の場合は、独自のコードでのみ演算子を定義できます。拡張性はありません。
これの最も一般的な使用法は、2つの列挙型の値を1つのグループにマージし、それらを同様に扱うことです。たとえば、 フルーツとベガテーブルを結合する の方法を参照してください。
列挙型は変装したクラスであるため、ほとんどの場合、列挙型で実行できるクラスでできることは何でもです。
列挙型がインターフェイスを実装できないという理由を考えることはできませんが、同時に、列挙型がインターフェイスを実装できない理由も考えられません。
インターフェイスやメソッドのようなものを列挙型に追加し始めたら、代わりにクラスにすることを本当に検討すべきだと思います。もちろん、私は非伝統的な列挙型のことを行うための有効なケースがあると確信しています。制限は人為的なものなので、私は人々に彼らが望むことをさせることに賛成です。
たとえば、Logger列挙型がある場合。次に、デバッグ、情報、警告、エラーなどのロガーメソッドをインターフェイスに含める必要があります。コードを疎結合にします。
インターフェイスで列挙型を使用するのに最適な使用例の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);
}
}
列挙型はJavaクラスに似ており、コンストラクタ、メソッドなどを持つことができます。それらを使用して実行できないことはnew EnumName()
だけです。インスタンスは、enum宣言で事前定義されています。
上記の投稿の戦略では、列挙型を使用した戦略パターンのニース軽量実装があなたに何をもたらすかについては十分に触れていませんでした:
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をニースの緩やかに型付けされた言語のように読み上げます!
これが私の理由です...
JavaFX ComboBoxにEnumの値を入力しました。 Identifiable(1つのメソッドを指定:識別)インターフェイスを使用して、オブジェクトを検索するためにアプリケーションに対して自分自身を識別する方法を指定できます。このインターフェイスを使用すると、あらゆる種類のオブジェクト(オブジェクトがIDに使用するフィールド)のリストをスキャンしてIDを一致させることができます。
ComboBoxリストでID値に一致するものを見つけたいです。 Enum値を含むComboBoxでこの機能を使用するには、EnumでIdentifiableインターフェイスを実装できる必要があります(たまたまEnumの場合は実装が簡単です)。
別の可能性:
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);
そこからインスタンス制御(各戦略はシングルトン)を維持するための戦略を記述するインターフェイスで内部列挙を使用しました。
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);
}
列挙型が戦略を実装するという事実は、列挙型クラスがその囲まれたインスタンスのプロキシとして機能できるようにするのに便利です。インターフェースも実装します。
これは一種のstrategyEnumProxy:Pで、クレントコードは次のようになります。
VectorizeStrategy.ConcreteStrategy.DEFAULT.processImage(img);
インターフェースを実装しなかった場合、次のようになりました。
VectorizeStrategy.ConcreteStrategy.DEFAULT.getInstance().processImage(img);