列挙型を特定の文字列と比較して存在するかどうかを確認する方法はありますか? 私はそのような機能を見つけることができないようです。メソッドを使用してvalueOf
例外をキャッチすることもできますが、実行時例外をキャッチすることはお勧めできません。誰にもアイデアはありますか?
10 に答える
これを行う必要がある場合は、名前を作成しSet<String>
たり、自分の名前を作成したりすることもありMap<String,MyEnum>
ます。それを確認するだけです。
注目に値するいくつかのポイント:
- そのような静的コレクションを静的初期化子に移入します。変数初期化子を使用せず、列挙型コンストラクターの実行時に実行されたことに依存しないでください。実行されていないことになります。(列挙型コンストラクターは、静的初期化子の前に最初に実行されるものです。)
- 頻繁に使用しないようにしてください
values()
。毎回新しいアレイを作成して設定する必要があります。すべての要素を反復処理するEnumSet.allOf
には、多数の要素がない列挙型に対してはるかに効率的なを使用します。
サンプルコード:
import java.util.*;
enum SampleEnum {
Foo,
Bar;
private static final Map<String, SampleEnum> nameToValueMap =
new HashMap<String, SampleEnum>();
static {
for (SampleEnum value : EnumSet.allOf(SampleEnum.class)) {
nameToValueMap.put(value.name(), value);
}
}
public static SampleEnum forName(String name) {
return nameToValueMap.get(name);
}
}
public class Test {
public static void main(String [] args)
throws Exception { // Just for simplicity!
System.out.println(SampleEnum.forName("Foo"));
System.out.println(SampleEnum.forName("Bar"));
System.out.println(SampleEnum.forName("Baz"));
}
}
もちろん、名前が数個しかない場合、これはおそらくやり過ぎです。nが十分に小さい場合、O(n)ソリューションがO(1)ソリューションに勝つことがよくあります。別のアプローチは次のとおりです。
import java.util.*;
enum SampleEnum {
Foo,
Bar;
// We know we'll never mutate this, so we can keep
// a local copy.
private static final SampleEnum[] copyOfValues = values();
public static SampleEnum forName(String name) {
for (SampleEnum value : copyOfValues) {
if (value.name().equals(name)) {
return value;
}
}
return null;
}
}
public class Test {
public static void main(String [] args)
throws Exception { // Just for simplicity!
System.out.println(SampleEnum.forName("Foo"));
System.out.println(SampleEnum.forName("Bar"));
System.out.println(SampleEnum.forName("Baz"));
}
}
例外をキャッチせずにそれを行う組み込みの方法はないと思います。代わりに次のようなものを使用できます。
public static MyEnum asMyEnum(String str) {
for (MyEnum me : MyEnum.values()) {
if (me.name().equalsIgnoreCase(str))
return me;
}
return null;
}
編集: Jon Skeet が指摘しvalues()
ているように、呼び出されるたびにプライベート バッキング アレイを複製することによって機能します。パフォーマンスが重要な場合は、values()
1 回だけ呼び出して配列をキャッシュし、それを反復処理することができます。
また、列挙型に膨大な数の値がある場合、Jon Skeet の代替マップは、どの配列反復よりも優れたパフォーマンスを発揮する可能性があります。
私のお気に入りのライブラリの1つ:ApacheCommons。
EnumUtilsはそれを簡単に行うことができます。
そのライブラリで列挙型を検証する例を次に示します。
public enum MyEnum {
DIV("div"), DEPT("dept"), CLASS("class");
private final String val;
MyEnum(String val) {
this.val = val;
}
public String getVal() {
return val;
}
}
MyEnum strTypeEnum = null;
// test if String str is compatible with the enum
// e.g. if you pass str = "div", it will return false. If you pass "DIV", it will return true.
if( EnumUtils.isValidEnum(MyEnum.class, str) ){
strTypeEnum = MyEnum.valueOf(str);
}
Jon Skeetの回答に基づいて、仕事で簡単にできるクラスを作成しました。
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Maps;
import java.util.EnumSet;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
/**
* <p>
* This permits to easily implement a failsafe implementation of the enums's valueOf
* Better use it inside the enum so that only one of this object instance exist for each enum...
* (a cache could solve this if needed)
* </p>
*
* <p>
* Basic usage exemple on an enum class called MyEnum:
*
* private static final FailSafeValueOf<MyEnum> FAIL_SAFE = FailSafeValueOf.create(MyEnum.class);
* public static MyEnum failSafeValueOf(String enumName) {
* return FAIL_SAFE.valueOf(enumName);
* }
*
* </p>
*
* <p>
* You can also use it outside of the enum this way:
* FailSafeValueOf.create(MyEnum.class).valueOf("EnumName");
* </p>
*
* @author Sebastien Lorber <i>(lorber.sebastien@gmail.com)</i>
*/
public class FailSafeValueOf<T extends Enum<T>> {
private final Map<String,T> nameToEnumMap;
private FailSafeValueOf(Class<T> enumClass) {
Map<String,T> map = Maps.newHashMap();
for ( T value : EnumSet.allOf(enumClass)) {
map.put( value.name() , value);
}
nameToEnumMap = ImmutableMap.copyOf(map);
}
/**
* Returns the value of the given enum element
* If the
* @param enumName
* @return
*/
public T valueOf(String enumName) {
return nameToEnumMap.get(enumName);
}
public static <U extends Enum<U>> FailSafeValueOf<U> create(Class<U> enumClass) {
return new FailSafeValueOf<U>(enumClass);
}
}
そしてユニットテスト:
import org.testng.annotations.Test;
import static org.testng.Assert.*;
/**
* @author Sebastien Lorber <i>(lorber.sebastien@gmail.com)</i>
*/
public class FailSafeValueOfTest {
private enum MyEnum {
TOTO,
TATA,
;
private static final FailSafeValueOf<MyEnum> FAIL_SAFE = FailSafeValueOf.create(MyEnum.class);
public static MyEnum failSafeValueOf(String enumName) {
return FAIL_SAFE.valueOf(enumName);
}
}
@Test
public void testInEnum() {
assertNotNull( MyEnum.failSafeValueOf("TOTO") );
assertNotNull( MyEnum.failSafeValueOf("TATA") );
assertNull( MyEnum.failSafeValueOf("TITI") );
}
@Test
public void testInApp() {
assertNotNull( FailSafeValueOf.create(MyEnum.class).valueOf("TOTO") );
assertNotNull( FailSafeValueOf.create(MyEnum.class).valueOf("TATA") );
assertNull( FailSafeValueOf.create(MyEnum.class).valueOf("TITI") );
}
}
Guavaを使用してImmutableMapを作成しましたが、実際には、マップが返されないため、通常のマップを使用できることに注意してください...
実行時例外をキャッチするのは悪いことだと誰かがあなたに言った理由がわかりません。
文字列を列挙型に変換/チェックするには、使用valueOf
とキャッチが問題ありません。IllegalArgumentException
Guava を使用して、次のようなこともできます。
// This method returns enum for a given string if it exists, otherwise it returns default enum.
private MyEnum getMyEnum(String enumName) {
// It is better to return default instance of enum instead of null
return hasMyEnum(enumName) ? MyEnum.valueOf(enumName) : MyEnum.DEFAULT;
}
// This method checks that enum for a given string exists.
private boolean hasMyEnum(String enumName) {
return Iterables.any(Arrays.asList(MyEnum.values()), new Predicate<MyEnum>() {
public boolean apply(MyEnum myEnum) {
return myEnum.name().equals(enumName);
}
});
}
2 番目の方法では、非常に便利なIterablesクラスを提供する guava ( Google Guava ) ライブラリを使用します。Iterables.any()メソッドを使用して、指定された値がリスト オブジェクトに存在するかどうかを確認できます。このメソッドには、リストとPredicateオブジェクトの 2 つのパラメーターが必要です。まず、 Arrays.asList()メソッドを使用して、すべての列挙型のリストを作成しました。その後、特定の要素 (この場合は列挙型) がapplyメソッドの条件を満たすかどうかを確認するために使用される新しいPredicateオブジェクトを作成しました。その場合、メソッドIterables.any()は真の値を返します。
指定された名前の列挙型定数が存在するかどうかを確認するために使用するものは次のとおりです。
java.util.Arrays.stream(E.values()).map(E::name).toList().contains("");
(あなたの列挙型が E と呼ばれているとします。) ここで "" の中に、それが列挙型で定義されているかどうかをチェックしたい列挙型定数の名前を入れる必要があります。配列をストリームに変換してからリストに変換するため、これは確かに最善の解決策ではありませんが、素晴らしく短く、私にとってはうまく機能します。
他の人が言ったように、2009 年にこの質問をしたので、2009 年以降、(新しいバージョンの Java に移行しない限り) あなたの場合、これは機能しません。Java は、この回答で使用されている機能をサポートしていません。しかし、新しいバージョンのJavaを使用している誰かがこれをやりたい場合に備えて、とにかく投稿しています。