定数 - java enum 数値
enumとint/Stringの間を便利にマップする (11)
限られた数の値しか取れない変数/パラメータを扱うとき、私はいつもJavaのenum
使用しようとします。
public enum BonusType {
MONTHLY, YEARLY, ONE_OFF
}
私のコードの中にいれば、うまく動作します。 しかし、同じ目的でプレーンなint
(またはString
)値を使用する他のコードとインターフェイスする必要があります。
その場合、両方の方法を変換できるように、各列挙型の値を整数に関連付ける便利な方法が必要です(つまり、「可逆列挙型」が必要です)。
enumからintへの移行は簡単です:
public enum BonusType {
public final int id;
BonusType(int id) {
this.id = id;
}
MONTHLY(1), YEARLY(2), ONE_OFF(3);
}
次に、 BonusType x = MONTHLY; int id = x.id;
としてint値にアクセスできますBonusType x = MONTHLY; int id = x.id;
BonusType x = MONTHLY; int id = x.id;
。
しかし、私は逆のための良い方法、すなわちintからenumに行くことはできません。 理想的には、次のようなもの
BonusType bt = BonusType.getById(2);
私が思いつくことができる唯一の解決策は次のとおりです。
- lookupメソッドをenumに
BonusType.values()
します。このメソッドは、BonusType.values()
を使用して "int - > enum"マップを塗りつぶし、それをキャッシュしてルックアップに使用します。 うまくいくでしょうが、私はこのメソッドを同じ列挙型に同じようにコピーしなければなりません:-(。 - Lookupメソッドを静的ユーティリティクラスに入れます。 それから、私はただ一つの "ルックアップ"メソッドしか必要としませんでしたが、私はそれを任意の列挙型のために働かせるためにリフレクションをする必要があります。
このような単純な(?)問題では、どちらの方法も非常に扱いにくいようです。
その他のアイデアや洞察?
逆enumの非常にクリーンな使用例
ステップ1 interface
EnumConverterを定義しinterface
。
public interface EnumConverter <E extends Enum<E> & EnumConverter<E>> {
public String convert();
E convert(String pKey);
}
ステップ2
クラス名を作成するReverseEnumMap
import java.util.HashMap;
import java.util.Map;
public class ReverseEnumMap<V extends Enum<V> & EnumConverter<V>> {
private Map<String, V> map = new HashMap<String, V>();
public ReverseEnumMap(Class<V> valueType) {
for (V v : valueType.getEnumConstants()) {
map.put(v.convert(), v);
}
}
public V get(String pKey) {
return map.get(pKey);
}
}
ステップ3
Enum
クラスに移動し、 EnumConverter<ContentType>
implement
します。もちろん、インターフェイスメソッドをオーバーライドします。 また、静的ReverseEnumMapを初期化する必要があります。
public enum ContentType implements EnumConverter<ContentType> {
VIDEO("Video"), GAME("Game"), TEST("Test"), IMAGE("Image");
private static ReverseEnumMap<ContentType> map = new ReverseEnumMap<ContentType>(ContentType.class);
private final String mName;
ContentType(String pName) {
this.mName = pName;
}
String value() {
return this.mName;
}
@Override
public String convert() {
return this.mName;
}
@Override
public ContentType convert(String pKey) {
return map.get(pKey);
}
}
ステップ4
次に、 Communication
クラスファイルを作成し、それをEnum
をString
およびString
に変換する新しいメソッドをEnum
に呼び出します。 私は説明のために主な方法を入れました。
public class Communication<E extends Enum<E> & EnumConverter<E>> {
private final E enumSample;
public Communication(E enumSample) {
this.enumSample = enumSample;
}
public String resolveEnumToStringValue(E e) {
return e.convert();
}
public E resolveStringEnumConstant(String pName) {
return enumSample.convert(pName);
}
//Should not put main method here... just for explanation purpose.
public static void main(String... are) {
Communication<ContentType> comm = new Communication<ContentType>(ContentType.GAME);
comm.resolveEnumToStringValue(ContentType.GAME); //return Game
comm.resolveStringEnumConstant("Game"); //return GAME (Enum)
}
}
org.apache.commons.lang.enums.ValuedEnum;
多くの定型的なコードを書いたり、Enumごとにコードを複製したりするために、代わりにApache Commons LangのValuedEnum
使用しました。
定義 :
public class NRPEPacketType extends ValuedEnum {
public static final NRPEPacketType TYPE_QUERY = new NRPEPacketType( "TYPE_QUERY", 1);
public static final NRPEPacketType TYPE_RESPONSE = new NRPEPacketType( "TYPE_RESPONSE", 2);
protected NRPEPacketType(String name, int value) {
super(name, value);
}
}
使用法:
int - > ValuedEnum:
NRPEPacketType packetType =
(NRPEPacketType) EnumUtils.getEnum(NRPEPacketType.class, 1);
http://www.javaspecialists.co.za/archive/Issue113.html
このソリューションは、enum定義の一部としてint値を使用した場合と似ています。 その後、ジェネリックスベースのルックアップユーティリティを作成します。
public class ReverseEnumMap<V extends Enum<V> & EnumConverter> {
private Map<Byte, V> map = new HashMap<Byte, V>();
public ReverseEnumMap(Class<V> valueType) {
for (V v : valueType.getEnumConstants()) {
map.put(v.convert(), v);
}
}
public V get(byte num) {
return map.get(num);
}
}
このソリューションはいいですし、すべてのenum型が暗黙的にEnumインタフェースを継承しているという事実に基づいているので、 'リフレクションに手を出す'必要はありません。
.ordinal()
とvalues()[i]
は両方ともenumの順序に依存するため不安定です。 したがって、列挙型の順序を変更したり、プログラムを追加/削除したりすると、プログラムが中断されます。
enumとintをマッピングするシンプルで効果的なメソッドを次に示します。
public enum Action {
ROTATE_RIGHT(0), ROTATE_LEFT(1), RIGHT(2), LEFT(3), UP(4), DOWN(5);
public final int id;
Action(int id) {
this.id = id;
}
public static Action get(int id){
for (Action a: Action.values()) {
if (a.id == id)
return a;
}
throw new IllegalArgumentException("Invalid id");
}
}
それを文字列に適用するのは難しいことではありません。
このコードでは、恒久的で強力な検索のために、メモリまたはプロセスを使用して、変換配列をインデックスとしてメモリを選択します。 私はそれが役に立ちそうです
public enum Test{
VALUE_ONE(101, "Im value one"),
VALUE_TWO(215, "Im value two");
private final int number;
private final byte[] desc;
private final static int[] converter = new int[216];
static{
Test[] st = values();
for(int i=0;i<st.length;i++){
cv[st[i].number]=i;
}
}
Test(int value, byte[] description) {
this.number = value;
this.desc = description;
}
public int value() {
return this.number;
}
public byte[] description(){
return this.desc;
}
public static String description(int value) {
return values()[converter[rps]].desc;
}
public static Test fromValue(int value){
return values()[converter[rps]];
}
}
この質問への回答は、Java 8のリリースで時代遅れです。
- データベースのようにJVMの外部に保持されている場合、序数は不安定であるため、序数は使用しないでください。
- キー値を使用して静的マップを作成するのは比較的簡単です。
public enum AccessLevel {
PRIVATE("private", 0),
PUBLIC("public", 1),
DEFAULT("default", 2);
AccessLevel(final String name, final int value) {
this.name = name;
this.value = value;
}
private final String name;
private final int value;
public String getName() {
return name;
}
public int getValue() {
return value;
}
static final Map<String, AccessLevel> names = Arrays.stream(AccessLevel.values())
.collect(Collectors.toMap(AccessLevel::getName, Function.identity()));
static final Map<Integer, AccessLevel> values = Arrays.stream(AccessLevel.values())
.collect(Collectors.toMap(AccessLevel::getValue, Function.identity()));
public static AccessLevel fromName(final String name) {
return names.get(name);
}
public static AccessLevel fromValue(final int value) {
return values.get(value);
}
}
受け入れられた答えが自己完結型ではないからといって、
サポートコード:
public interface EnumWithCode<E extends Enum<E> & EnumWithCode<E>> {
public Integer getCode();
E fromCode(Integer code);
}
public class EnumWithCodeMap<V extends Enum<V> & EnumWithCode<V>> {
private final HashMap<Integer, V> _map = new HashMap<Integer, V>();
public EnumWithCodeMap(Class<V> valueType) {
for( V v : valueType.getEnumConstants() )
_map.put(v.getCode(), v);
}
public V get(Integer num) {
return _map.get(num);
}
}
使用例:
public enum State implements EnumWithCode<State> {
NOT_STARTED(0), STARTED(1), ENDED(2);
private static final EnumWithCodeMap<State> map = new EnumWithCodeMap<State>(
State.class);
private final int code;
private State(int code) {
this.code = code;
}
@Override
public Integer getCode() {
return code;
}
@Override
public State fromCode(Integer code) {
return map.get(code);
}
}
完全性のために、ここでは、任意の列挙型からインデックスで列挙値を取得する一般的な方法を示します。 私の意図は、メソッドをEnum.valueOf(Class、String)のように見えるようにすることでした 。 Fyi、私はhereからこのメソッドをコピーしhere 。
インデックス関連の問題(既にここで詳しく説明しています)は引き続き適用されます。
/**
* Returns the {@link Enum} instance for a given ordinal.
* This method is the index based alternative
* to {@link Enum#valueOf(Class, String)}, which
* requires the name of an instance.
*
* @param <E> the enum type
* @param type the enum class object
* @param ordinal the index of the enum instance
* @throws IndexOutOfBoundsException if ordinal < 0 || ordinal >= enums.length
* @return the enum instance with the given ordinal
*/
public static <E extends Enum<E>> E valueOf(Class<E> type, int ordinal) {
Preconditions.checkNotNull(type, "Type");
final E[] enums = type.getEnumConstants();
Preconditions.checkElementIndex(ordinal, enums.length, "ordinal");
return enums[ordinal];
}
私はこれをWeb上で見つけました。実装するのは非常に便利で簡単でした。 この解決策は私によって作られたものではありません
http://www.ajaxonomy.com/2007/java/making-the-most-of-java-50-enum-tricks
public enum Status {
WAITING(0),
READY(1),
SKIPPED(-1),
COMPLETED(5);
private static final Map<Integer,Status> lookup
= new HashMap<Integer,Status>();
static {
for(Status s : EnumSet.allOf(Status.class))
lookup.put(s.getCode(), s);
}
private int code;
private Status(int code) {
this.code = code;
}
public int getCode() { return code; }
public static Status get(int code) {
return lookup.get(code);
}
}
私はそれがJavaで同じかどうかは分かりませんが、Cの列挙型も自動的に整数にマップされるので、型または整数のどちらかを使用してアクセスできます。 単純に整数でアクセスしようとしましたか?
Int -->String :
public enum Country {
US("US",0),
UK("UK",2),
DE("DE",1);
private static Map<Integer, String> domainToCountryMapping;
private String country;
private int domain;
private Country(String country,int domain){
this.country=country.toUpperCase();
this.domain=domain;
}
public String getCountry(){
return country;
}
public static String getCountry(String domain) {
if (domainToCountryMapping == null) {
initMapping();
}
if(domainToCountryMapping.get(domain)!=null){
return domainToCountryMapping.get(domain);
}else{
return "US";
}
}
private static void initMapping() {
domainToCountryMapping = new HashMap<Integer, String>();
for (Country s : values()) {
domainToCountryMapping.put(s.domain, s.country);
}
}