回答:
序数を列挙型に変換するには、次のようにします。
ReportTypeEnum value = ReportTypeEnum.values()[ordinal];
配列の境界に注意してください。
を呼び出すたびにvalues()
新しくクローンされた配列が返され、パフォーマンスに悪影響を与える可能性があることに注意してください。頻繁に呼び出される場合は、配列をキャッシュすることをお勧めします。
この回答は、コメント内のフィードバックを含むように編集されました
これはほぼ間違いなく悪い考えです。確かに、序数が事実上存続している場合(たとえば、誰かがURLをブックマークしたため)-これはenum
、将来、常に順序を保持する必要があることを意味します。
代わりにenum
使用してエンコードmyEnumValue.name()
(そしてを介してデコードReportTypeEnum.valueOf(s)
)しないのはなぜですか?
value
、最初またはその正しいアルファベット/論理的な位置にを追加するよりもはるかに可能性が低いと思います。(論理的には、たとえばTimeUnit
値に論理的な位置があることを意味します)
私がvalues()
たくさん使うつもりなら:
enum Suit {
Hearts, Diamonds, Spades, Clubs;
public static final Suit values[] = values();
}
一方wherewhere.java:
Suit suit = Suit.values[ordinal];
配列の境界に注意してください。
Suit.values[0] = Suit.Diamonds;
、コードのどこかを妨げるものではありません。理想的にはそれは決して起こらないでしょうが、変更可能なフィールドを公開しないという全体的な原則はまだ成り立ちます。このアプローチでは、Collections.unmodifiableList
代わりになどを使用することを検討してください。
Suit values[]
直接的または間接的に公開されません(どのようにを通じて言及されたかgetValues()
)。ordinal
値をどのように引数に送信し、Suit
から表現を返す必要があるパブリックメソッドで回避しSuit values[]
ます。ここでのポイント(最初からの質問のタイル)は、列挙型序数から列挙型を作成することでした
序数を使用することはおそらく悪い考えであることを私はほとんどの人に同意します。通常、この問題を解決するには、列挙型に、たとえばDB値を取得できるプライベートコンストラクターを指定しfromDbValue
、Janの答えに似た静的関数を作成します。
public enum ReportTypeEnum {
R1(1),
R2(2),
R3(3),
R4(4),
R5(5),
R6(6),
R7(7),
R8(8);
private static Logger log = LoggerFactory.getLogger(ReportEnumType.class);
private static Map<Integer, ReportTypeEnum> lookup;
private Integer dbValue;
private ReportTypeEnum(Integer dbValue) {
this.dbValue = dbValue;
}
static {
try {
ReportTypeEnum[] vals = ReportTypeEnum.values();
lookup = new HashMap<Integer, ReportTypeEnum>(vals.length);
for (ReportTypeEnum rpt: vals)
lookup.put(rpt.getDbValue(), rpt);
}
catch (Exception e) {
// Careful, if any exception is thrown out of a static block, the class
// won't be initialized
log.error("Unexpected exception initializing " + ReportTypeEnum.class, e);
}
}
public static ReportTypeEnum fromDbValue(Integer dbValue) {
return lookup.get(dbValue);
}
public Integer getDbValue() {
return this.dbValue;
}
}
これで、ルックアップを変更せずに順序を変更でき、その逆も可能です。
あなたは可能性があり、静的なルックアップテーブルを使用します。
public enum Suit {
spades, hearts, diamonds, clubs;
private static final Map<Integer, Suit> lookup = new HashMap<Integer, Suit>();
static{
int ordinal = 0;
for (Suit suit : EnumSet.allOf(Suit.class)) {
lookup.put(ordinal, suit);
ordinal+= 1;
}
}
public Suit fromOrdinal(int ordinal) {
return lookup.get(ordinal);
}
}
0...(n-1)
場合、配列はコードが少なく、読みやすくなります。パフォーマンスの向上は単なるボーナスです。private static final Suit[] VALUES = values();
とpublic Suit fromOrdinal(int ordinal) { return VALUES[ordinal]; }
。追加の利点:暗黙的にnullを返すのではなく、無効な序数ですぐにクラッシュします。(常に利点があるとは限りません。しかし、多くの場合。)
これは私が使用するものです。私は、上記の簡単なソリューションよりも「効率的」ではないことを見せかけていません。それが行うことは、上記のソリューションで無効な序数値が使用されている場合に、「ArrayIndexOutOfBounds」よりもはるかに明確な例外メッセージを提供することです。
EnumSet javadocがイテレータが要素を自然な順序で返すことを指定しているという事実を利用しています。それが正しくない場合はアサートがあります。
JUnit4テストは、それがどのように使用されるかを示しています。
/**
* convert ordinal to Enum
* @param clzz may not be null
* @param ordinal
* @return e with e.ordinal( ) == ordinal
* @throws IllegalArgumentException if ordinal out of range
*/
public static <E extends Enum<E> > E lookupEnum(Class<E> clzz, int ordinal) {
EnumSet<E> set = EnumSet.allOf(clzz);
if (ordinal < set.size()) {
Iterator<E> iter = set.iterator();
for (int i = 0; i < ordinal; i++) {
iter.next();
}
E rval = iter.next();
assert(rval.ordinal() == ordinal);
return rval;
}
throw new IllegalArgumentException("Invalid value " + ordinal + " for " + clzz.getName( ) + ", must be < " + set.size());
}
@Test
public void lookupTest( ) {
java.util.concurrent.TimeUnit tu = lookupEnum(TimeUnit.class, 3);
System.out.println(tu);
}
これは私がAndroidでProguardを使って行うことです。
public enum SomeStatus {
UNINITIALIZED, STATUS_1, RESERVED_1, STATUS_2, RESERVED_2, STATUS_3;//do not change order
private static SomeStatus[] values = null;
public static SomeStatus fromInteger(int i) {
if(SomeStatus.values == null) {
SomeStatus.values = SomeStatus.values();
}
if (i < 0) return SomeStatus.values[0];
if (i >= SomeStatus.values.length) return SomeStatus.values[0];
return SomeStatus.values[i];
}
}
それは短く、私はプロガードで例外があることを心配する必要はありません
public enum Suit implements java.io.Serializable, Comparable<Suit>{
spades, hearts, diamonds, clubs;
private static final Suit [] lookup = Suit.values();
public Suit fromOrdinal(int ordinal) {
if(ordinal< 1 || ordinal> 3) return null;
return lookup[value-1];
}
}
テストクラス
public class MainTest {
public static void main(String[] args) {
Suit d3 = Suit.diamonds;
Suit d3Test = Suit.fromOrdinal(2);
if(d3.equals(d3Test)){
System.out.println("Susses");
}else System.out.println("Fails");
}
}
より効率的なコードがある場合は、私たちと共有していただきありがとうございます。私の列挙型は巨大で、常に何千回も呼び出されます。
したがって、1つの方法は、どちらExampleEnum valueOfOrdinal = ExampleEnum.values()[ordinal];
が機能するかを簡単にすることですが、前に述べたように、ExampleEnum.values()
呼び出しごとに新しい複製された配列を返します。それは不必要に高価になる可能性があります。このように配列をキャッシュすることで解決できExampleEnum[] values = values()
ます。また、キャッシュされた配列を変更できるようにすることも「危険」です。誰かが書くExampleEnum.values[0] = ExampleEnum.type2;
ことができるので、余分なコピーを行わないアクセサメソッドでプライベートにします。
private enum ExampleEnum{
type0, type1, type2, type3;
private static final ExampleEnum[] values = values();
public static ExampleEnum value(int ord) {
return values[ord];
}
}
にExampleEnum.value(ordinal)
関連付けられた列挙値を取得するために使用しますordinal
すべてのenumにはname()があり、enumメンバーの名前の文字列を提供します。
与えられenum Suit{Heart, Spade, Club, Diamond}
、Suit.Heart.name()
与えHeart
ます。
すべての列挙valueOf()
型には、列挙型と文字列を取り、逆の操作を実行するメソッドがあります。
Enum.valueOf(Suit.class, "Heart")
を返しますSuit.Heart
。
なぜ誰もが序数を使用する理由は私を超えています。別の開発者が一部のコードが序数値に依存していることに気付かない可能性があるため(特に質問で引用されているJSPページでは、ネットワークとデータベースのオーバーヘッドが完全に支配しているため)時間、文字列に整数を使用しない)。