質問

このインターフェイスを実装する列挙型が多数あります。

/**
 * Interface for an enumeration, each element of which can be uniquely identified by it's code
 */
public interface CodableEnum {

    /**
     * Get the element with a particular code
     * @param code
     * @return
     */
    public CodableEnum getByCode(String code);

    /**
     * Get the code that identifies an element of the enum
     * @return
     */
    public String getCode();
}

典型的な例は次のとおりです。

public enum IMType implements CodableEnum {

    MSN_MESSENGER("msn_messenger"),
    GOOGLE_TALK("google_talk"),
    SKYPE("skype"),
    YAHOO_MESSENGER("yahoo_messenger");

    private final String code;

    IMType (String code) {
        this.code = code;
    }

    public String getCode() {
        return code;
    }   

    public IMType getByCode(String code) {
        for (IMType e : IMType.values()) {
            if (e.getCode().equalsIgnoreCase(code)) {
                return e;
            }
        }
    }
}

ご想像のとおり、これらのメソッドは CodableEnum のすべての実装で実質的に同一です。この重複を削除したいのですが、正直、方法がわかりません。以下のようなクラスを使ってみました。

public abstract class DefaultCodableEnum implements CodableEnum {

    private final String code;

    DefaultCodableEnum(String code) {
        this.code = code;
    }

    public String getCode() {
        return this.code;
    }   

    public abstract CodableEnum getByCode(String code);  
}

しかし、これは次の理由からまったく役に立たないことがわかります。

  1. enum はクラスを拡張できません
  2. 列挙型の要素 (SKYPE、GOOGLE_TALK など) はクラスを拡張できません
  3. DefaultCodableEnum 自体は Enum ではないため、getByCode() のデフォルト実装を提供できません。DefaultCodableEnum を変更して java.lang.Enum を拡張しようとしましたが、これは許可されていないようです。

反省に頼らない提案はありますか?ありがとう、ドン

役に立ちましたか?

解決

重複したコードを因数分解して、 CodeableEnumHelper クラス:

public class CodeableEnumHelper {
    public static CodeableEnum getByCode(String code, CodeableEnum[] values) {
        for (CodeableEnum e : values) {
            if (e.getCode().equalsIgnoreCase(code)) {
                return e;
            }
        }
        return null;
    }
}

それぞれ CodeableEnum クラスはまだ実装する必要があります getByCode メソッドですが、メソッドの実際の実装は少なくとも 1 か所に集中化されています。

public enum IMType implements CodeableEnum {
    ...
    public IMType getByCode(String code) {
        return (IMType)CodeableEnumHelper.getByCode(code, this.values());
    } 
}

他のヒント

抽象列挙型は潜在的に非常に便利です (そして現在は許可されていません)。ただし、Sun の誰かに追加するように働きかけたい場合は、提案とプロトタイプが存在します。

http://freddy33.blogspot.com/2007/11/abstract-enum-ricky-carlson-way.html

サンRFE:

http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6570766

dave のコードを整理するには:

public class CodeableEnumHelper {
    public static <E extends CodeableEnum> E getByCode(
        String code, E[] values
    ) {
        for (E e : values) {
            if (e.getCode().equalsIgnoreCase(code)) {
                return e;
            }
        }
        return null;
    }
}

public enum IMType implements CodableEnum {
    ...
    public IMType getByCode(String code) {
        return CodeableEnumHelper.getByCode(code, values());
    } 
}

または、より効率的に:

public class CodeableEnumHelper {
    public static <E extends CodeableEnum> Map<String,E> mapByCode(
        E[] values
    ) {
        Map<String,E> map = new HashMap<String,E>();
        for (E e : values) {
            map.put(e.getCode().toLowerCase(Locale.ROOT), value) {
        }
        return map;
    }
}

public enum IMType implements CodableEnum {
    ...
    private static final Map<String,IMType> byCode =
        CodeableEnumHelper.mapByCode(values());
    public IMType getByCode(String code) {
        return byCode.get(code.toLowerCase(Locale.ROOT));
    } 
}

私が作成したローカリゼーション コンポーネントでも同様の問題がありました。私のコンポーネントは、リソース バンドルにインデックスを付ける enum 定数を使用してローカライズされたメッセージにアクセスするように設計されていますが、難しい問題ではありません。

同じ「テンプレート」列挙型コードをコピーしてあちこちに貼り付けていたことがわかりました。重複を避けるための私の解決策は、enum 定数名とコンストラクター引数を含む XML 構成ファイルを受け入れるコード ジェネレーターです。出力は、「重複した」動作を含む Java ソース コードです。

現在、私は重複したコードのすべてではなく、構成ファイルとジェネレーターを保守しています。enum ソース コードがあったはずの場所には、どこにでも XML 構成ファイルが存在します。私のビルド スクリプトは、生成された古いファイルを検出し、コード ジェネレーターを呼び出して enum コードを作成します。

このコンポーネントが表示されます ここ. 。コピーして貼り付けていたテンプレートは除外されます XSLT スタイルシート. 。の コードジェネレーター スタイルシート変換を実行します。アン 入力ファイル 生成された enum ソース コードと比較すると、非常に簡潔です。

HTH、
グレッグ

残念ながら、これを行う方法はないと思います。おそらく最善の策は、エミュを完全に諦めて、従来のクラス拡張と静的メンバーを使用することでしょう。それ以外の場合は、そのコードを複製することに慣れてください。ごめん。

コードによって列挙型をロードするタイプセーフなユーティリティ クラスを作成します。

インターフェイスは次のようになります。

public interface CodeableEnum {
    String getCode();
}

ユーティリティ クラスは次のとおりです。

import java.lang.reflect.InvocationTargetException;


public class CodeableEnumUtils {
    @SuppressWarnings("unchecked")
    public static <T extends CodeableEnum>  T getByCode(String code, Class<T> enumClass) throws IllegalArgumentException, SecurityException, IllegalAccessException, InvocationTargetException, NoSuchMethodException {
        T[] allValues = (T[]) enumClass.getMethod("values", new Class[0]).invoke(null, new Object[0]);
        for (T value : allValues) {
            if (value.getCode().equals(code)) {
                return value;
            }
        }
        return null;
}

}

使用法を示すテスト ケース:

import junit.framework.TestCase;


public class CodeableEnumUtilsTest extends TestCase {
    public void testWorks() throws Exception {
    assertEquals(A.ONE, CodeableEnumUtils.getByCode("one", A.class));
      assertEquals(null, CodeableEnumUtils.getByCode("blah", A.class));
    }

enum A implements CodeableEnum {
    ONE("one"), TWO("two"), THREE("three");

    private String code;

    private A(String code) {
        this.code = code;
    }

    public String getCode() {
        return code;
    }   
}
}

これで、getCode() メソッドのみが複製され、getByCode() メソッドは 1 か所にまとめられました。すべての例外を 1 つの RuntimeException にラップするのも良いかもしれません :)

ここに別の解決策があります。

interface EnumTypeIF {
String getValue();

EnumTypeIF fromValue(final String theValue);

EnumTypeIF[] getValues();

class FromValue {
  private FromValue() {
  }

  public static EnumTypeIF valueOf(final String theValue, EnumTypeIF theEnumClass) {

    for (EnumTypeIF c : theEnumClass.getValues()) {
      if (c.getValue().equals(theValue)) {
        return c;
      }
    }
    throw new IllegalArgumentException(theValue);
  }
}

重要なのは、内部クラスを使用して「グローバル メソッド」を保持できることです。

私にとってはかなりうまくいきました。OK、3つの方法を実装する必要がありますが、これらの方法は単なる委任者です。

実際には実行時の型情報を実装しているようです。Java はこれを言語機能として提供します。

RTTI またはリフレクションを調べることをお勧めします。

そんなことは不可能だと思います。ただし、列挙型値の名前をコードとして使用する場合は、列挙型の valueOf(String name) メソッドを使用できます。

静的ジェネリックメソッドはどうでしょうか?enum の getByCode() メソッド内から再利用することも、単に直接使用することもできます。私は列挙型に常に整数 ID を使用するため、getById() メソッドはこれを実行するだけです。戻り値()[id]。はるかに速くて簡単です。

本当に継承が必要な場合は、継承できることを忘れないでください。 enum パターンを自分で実装する, 、古き良き Java 1.4 の頃のように。

あなたが望んでいることにほぼ近づいたのは、(enum の valueOf(String name) を使用して) 汎用コードを「実装」するテンプレートを IntelliJ で作成することでした。完璧ではありませんが、非常にうまく機能します。

あなたの特定のケースでは、getCode() / getByCode(String code) メソッドは、すべての列挙によって提供される toString() / valueOf(String value) メソッドの動作に(婉曲的に言えば)非常に近いように見えます。なぜそれらを使用したくないのですか?

別の解決策は、列挙型自体には何も入れず、列挙型ごとに双方向マップ Enum <-> コードを提供するだけです。たとえば、次のようにすることができます。使用 不変BiMap これについては Google コレクションから。

そうすれば、重複したコードはまったくなくなります。

例:

public enum MYENUM{
  VAL1,VAL2,VAL3;
}

/** Map MYENUM to its ID */
public static final ImmutableBiMap<MYENUM, Integer> MYENUM_TO_ID = 
new ImmutableBiMap.Builder<MYENUM, Integer>().
put(MYENUM.VAL1, 1).
put(MYENUM.VAL2, 2).
put(MYENUM.VAL3, 3).
build();

私の意見では、これはリフレクションや列挙型に追加のラッパーを追加することなく、最も簡単な方法だと思います。

enum が実装するインターフェイスを作成します。

public interface EnumWithId {

    public int getId();

}

次に、ヘルパー クラスで次のようなメソッドを作成するだけです。

public <T extends EnumWithId> T getById(Class<T> enumClass, int id) {
    T[] values = enumClass.getEnumConstants();
    if (values != null) {
        for (T enumConst : values) {
            if (enumConst.getId() == id) {
                return enumConst;
            }
        }
    }

    return null;
}

このメソッドは次のように使用できます。

MyUtil.getInstance().getById(MyEnum.class, myEnumId);
ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top