23

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

/**
 * Interface for an enumeration, each element of which can be uniquely identified by its 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. 列挙型はクラスを拡張できません
  2. 列挙型の要素 (SKYPE、GOOGLE_TALK など) はクラスを拡張できません
  3. DefaultCodableEnum 自体は Enum ではないため、getByCode() のデフォルトの実装を提供できません。DefaultCodableEnum を変更して java.lang.Enum を拡張しようとしましたが、これは許可されていないようです。

リフレクションに頼らない提案はありますか?ありがとう、ドン

4

15 に答える 15

13

複製されたコードを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());
    } 
}
于 2008-09-16T23:22:40.837 に答える
7

抽象列挙型は潜在的に非常に有用です(現在は許可されていません)。しかし、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

于 2008-09-16T21:43:23.210 に答える
5

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));
    } 
}
于 2008-09-17T10:41:26.227 に答える
2

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

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

現在、複製されたコードのすべてではなく、構成ファイルとジェネレーターを維持しています。列挙型のソース コードがあればどこにでも、XML 構成ファイルがあります。私のビルド スクリプトは古い生成ファイルを検出し、コード ジェネレーターを呼び出して列挙型コードを作成します。

このコンポーネントはこちらで確認できます。コピーして貼り付けていたテンプレートは、XSLT スタイルシートに分解されます。コード ジェネレーターがスタイルシート変換を実行します。入力ファイルは、生成されたenum ソース コードに比べて非常に簡潔です。

HTH、
グレッグ

于 2008-09-16T21:45:25.220 に答える
1

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

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つのメソッドを実装する必要がありますが、これらのメソッドは単なる委任者です。

于 2011-03-20T11:02:39.660 に答える
1

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

于 2008-09-16T21:24:58.060 に答える
1

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

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

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 か所にまとめられました。すべての例外を単一の RuntimeException にラップするのもいいかもしれません:)

于 2008-09-17T00:02:55.980 に答える
0

特定のケースでは、getCode()/ getByCode(String code)メソッドは、すべての列挙型によって提供されるtoString()/ valueOf(String value)メソッドの動作に非常に近いように見えます(婉曲的に言えば)。使ってみませんか?

于 2008-09-17T11:00:54.277 に答える
0

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

于 2008-09-16T22:39:10.363 に答える
0

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

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

于 2008-09-16T21:28:28.863 に答える
0

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

于 2008-09-16T21:33:20.707 に答える
0

静的ジェネリック メソッドはどうですか? 列挙型の getByCode() メソッド内から再利用するか、直接使用することができます。私は常に列挙型に整数 ID を使用しているため、getById() メソッドはこれを行うだけです: return values()[id]. それはずっと速くて簡単です。

于 2008-09-16T21:37:06.610 に答える
0

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

于 2008-09-16T21:53:48.210 に答える
0

別の解決策は、列挙型自体に何も入れず、各列挙型に双方向マップ Enum <-> Code を提供するだけです。たとえば、これには Google Collections のImmutableBiMapを使用できます。

そうすれば、重複するコードはまったくありません。

例:

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();
于 2010-03-18T17:06:14.580 に答える
0

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

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

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);
于 2010-05-19T15:42:13.547 に答える