JavaにおけるEnum
Java 1.5で、定数の固定セットからなる新しいタイプとしてEnumが導入されました。例えば、Java Enumとして方角をEAST、WEST、NORTH、そしてSOUTHという固定フィールドで作成することができます。
Javaの列挙型
Java Enum の例
Javaのenumキーワードは、enumタイプを作成するために使用されます。Javaのenumの例題プログラムを見てみましょう。
package com.scdev.enums;
public enum ThreadStates {
START,
RUNNING,
WAITING,
DEAD;
}
上記の例では、ThreadStatesは固定の定数フィールド、「START」、「RUNNING」、「WAITING」および「DEAD」を持つ列挙型です。
JavaのEnumと定数の違いは何ですか?
さて、Javaのenumが通常の定数フィールドよりも優れているのを見てみましょう。Javaクラスで似たような定数クラスを作成しましょう。
package com.scdev.enums;
public class ThreadStatesConstant {
public static final int START = 1;
public static final int WAITING = 2;
public static final int RUNNING = 3;
public static final int DEAD = 4;
}
では、Javaプログラムでenumと定数がどのように使われるかを見てみましょう。
/**
* This method shows the benefit of using Enum over Constants
*/
private static void benefitsOfEnumOverConstants() {
//Enum values are fixed
simpleEnumExample(ThreadStates.START);
simpleEnumExample(ThreadStates.WAITING);
simpleEnumExample(ThreadStates.RUNNING);
simpleEnumExample(ThreadStates.DEAD);
simpleEnumExample(null);
simpleConstantsExample(1);
simpleConstantsExample(2);
simpleConstantsExample(3);
simpleConstantsExample(4);
//we can pass any int constant
simpleConstantsExample(5);
}
private static void simpleEnumExample(ThreadStates th) {
if(th == ThreadStates.START) System.out.println("Thread started");
else if (th == ThreadStates.WAITING) System.out.println("Thread is waiting");
else if (th == ThreadStates.RUNNING) System.out.println("Thread is running");
else System.out.println("Thread is dead");
}
private static void simpleConstantsExample(int i) {
if(i == ThreadStatesConstant.START) System.out.println("Thread started");
else if (i == ThreadStatesConstant.WAITING) System.out.println("Thread is waiting");
else if (i == ThreadStatesConstant.RUNNING) System.out.println("Thread is running");
else System.out.println("Thread is dead");
}
上記の例を見ると、定数を使用する場合には解決できない2つのリスクがありますが、enumを使用することでそれらを解決できます。
-
- シンプルな定数の例メソッドには任意のint定数を渡すことができますが、simpleEnumExampleには固定値のみを渡すことができますので、型の安全性が提供されます。
- ThreadStatesConstantクラスのint定数の値を変更しても、上記のプログラムは例外をスローしません。プログラムは予期しない動作をするかもしれませんが、enum定数を変更すると、実行時の問題の可能性を排除するコンパイル時エラーが発生します。
Javaのenumのメソッド
では、例を使ってJavaのenumのさらなる機能を見てみましょう。
package com.scdev.enums;
import java.io.Closeable;
import java.io.IOException;
/**
* This Enum example shows all the things we can do with Enum types
*
*/
public enum ThreadStatesEnum implements Closeable{
START(1){
@Override
public String toString(){
return "START implementation. Priority="+getPriority();
}
@Override
public String getDetail() {
return "START";
}
},
RUNNING(2){
@Override
public String getDetail() {
return "RUNNING";
}
},
WAITING(3){
@Override
public String getDetail() {
return "WAITING";
}
},
DEAD(4){
@Override
public String getDetail() {
return "DEAD";
}
};
private int priority;
public abstract String getDetail();
//Enum constructors should always be private.
private ThreadStatesEnum(int i){
priority = i;
}
//Enum can have methods
public int getPriority(){
return this.priority;
}
public void setPriority(int p){
this.priority = p;
}
//Enum can override functions
@Override
public String toString(){
return "Default ThreadStatesConstructors implementation. Priority="+getPriority();
}
@Override
public void close() throws IOException {
System.out.println("Close of Enum");
}
}
Javaの列挙型の重要なポイント
以下は、JavaにおけるEnumsの重要なポイントの一部です。
-
- すべてのJavaの列挙型は、暗黙的にjava.lang.Enumクラスを拡張しており、このクラスはObjectクラスを拡張し、SerializableとComparableのインターフェースを実装しています。そのため、列挙型では他のクラスを拡張することはできません。
列挙型はキーワードなので、パッケージ名の末尾には使用できません。例えばcom.scdev.enumは有効なパッケージ名ではありません。
列挙型はインターフェースを実装することができます。例えば、上記の列挙型の例ではCloseableインターフェースを実装しています。
列挙型のコンストラクタは常にprivateです。
new演算子を使用して列挙型のインスタンスを作成することはできません。
Javaの列挙型では抽象メソッドを宣言することができますが、その場合は全ての列挙型のフィールドがその抽象メソッドを実装しなければなりません。上記の例ではgetDetail()が抽象メソッドであり、全ての列挙型のフィールドがそれを実装しています。
列挙型内でメソッドを定義することができ、列挙型のフィールドもそれをオーバーライドすることができます。例えば、toString()メソッドは列挙型で定義され、列挙型のフィールドであるSTARTでもそれをオーバーライドしています。
Javaの列挙型のフィールドには名前空間があり、ThreadStates.STARTのようにクラス名と共に列挙型のフィールドを使用することができます。
列挙型はswitch文で使用することができます。このチュートリアルの後半で実際の使用例を見ることになります。
既存の列挙型を拡張することは、既存の機能に影響を与えることなく可能です。例えば、ThreadStates列挙型に新しいフィールドNEWを追加することができます。
列挙型のフィールドは定数であり、Javaのベストプラクティスでは大文字とアンダースコアを使用して命名することが推奨されています。例えばEAST、WEST、EAST_DIRECTIONなどです。
列挙型の定数は暗黙的にstaticおよびfinalです。
列挙型の定数はfinalですが、その変数は変更することができます。例えば、setPriority()メソッドを使用して列挙型の定数の優先度を変更することができます。以下の例でそれを使用する方法を見てみましょう。
列挙型の定数はfinalなので、「==」演算子やequals()メソッドを使用して安全に比較することができます。両者の結果は同じになります。
JavaのEnumSet、EnumMap、valueOf()
Enumのほとんどの機能を知っているので、JavaのEnumの例題プログラムを見てみましょう。その後、enumのさらなる機能について学びましょう。
package com.scdev.enums;
import java.io.IOException;
import java.util.EnumMap;
import java.util.EnumSet;
import java.util.Set;
public class JavaEnumExamples {
public static void main(String[] args) throws IOException {
usingEnumMethods();
usingEnumValueOf();
usingEnumValues();
usingEnumInSwitch(ThreadStatesEnum.START);
usingEnumInSwitch(ThreadStatesEnum.DEAD);
usingEnumMap();
usingEnumSet();
}
private static void usingEnumSet() {
EnumSet enumSet = EnumSet.allOf(ThreadStatesEnum.class);
for(ThreadStatesEnum tsenum : enumSet){
System.out.println("Using EnumSet, priority = "+tsenum.getPriority());
}
}
private static void usingEnumMap() {
EnumMap<ThreadStatesEnum, String> enumMap = new EnumMap<ThreadStatesEnum,String>(ThreadStatesEnum.class);
enumMap.put(ThreadStatesEnum.START, "Thread is started");
enumMap.put(ThreadStatesEnum.RUNNING, "Thread is running");
enumMap.put(ThreadStatesEnum.WAITING, "Thread is waiting");
enumMap.put(ThreadStatesEnum.DEAD, "Thread is dead");
Set keySet = enumMap.keySet();
for(ThreadStatesEnum key : keySet){
System.out.println("key="+key.toString()+":: value="+enumMap.get(key));
}
}
private static void usingEnumInSwitch(ThreadStatesEnum th) {
switch (th){
case START:
System.out.println("START thread");
break;
case WAITING:
System.out.println("WAITING thread");
break;
case RUNNING:
System.out.println("RUNNING thread");
break;
case DEAD:
System.out.println("DEAD thread");
}
}
private static void usingEnumValues() {
ThreadStatesEnum[] thArray = ThreadStatesEnum.values();
for(ThreadStatesEnum th : thArray){
System.out.println(th.toString() + "::priority="+th.getPriority());
}
}
private static void usingEnumValueOf() {
ThreadStatesEnum th = Enum.valueOf(ThreadStatesEnum.class, "START");
System.out.println("th priority="+th.getPriority());
}
private static void usingEnumMethods() throws IOException {
ThreadStatesEnum thc = ThreadStatesEnum.DEAD;
System.out.println("priority is:"+thc.getPriority());
thc = ThreadStatesEnum.DEAD;
System.out.println("Using overriden method."+thc.toString());
thc = ThreadStatesEnum.START;
System.out.println("Using overriden method."+thc.toString());
thc.setPriority(10);
System.out.println("Enum Constant variable changed priority value="+thc.getPriority());
thc.close();
}
}
enumの他の重要な機能を説明する前に、上記プログラムの出力を見てみましょう。
priority is:4
Using overriden method.Default ThreadStatesConstructors implementation. Priority=4
Using overriden method.START implementation. Priority=1
Enum Constant variable changed priority value=10
Close of Enum
th priority=10
START implementation. Priority=10::priority=10
Default ThreadStatesConstructors implementation. Priority=2::priority=2
Default ThreadStatesConstructors implementation. Priority=3::priority=3
Default ThreadStatesConstructors implementation. Priority=4::priority=4
START thread
DEAD thread
key=START:: value=Thread is started
key=RUNNING:: value=Thread is running
key=WAITING:: value=Thread is waiting
key=DEAD:: value=Thread is dead
Using EnumSet, priority = 10
Using EnumSet, priority = 2
Using EnumSet, priority = 3
Using EnumSet, priority = 4
重要なポイント
-
- usingEnumMethods()メソッドは、enumオブジェクトを作成し、そのメソッドを使用する方法を示しています。また、setPriority(int i)メソッドを使用してenumの変数を変更する方法も示しています。
-
- usingEnumValueOf()は、java.util.Enum valueOf(enumType、name)の使用方法を示しており、これを使用して文字列からenumオブジェクトを作成できます。指定されたenumタイプに指定された名前の定数がない場合、または指定されたクラスオブジェクトがenumタイプを表していない場合、IllegalArgumentExceptionがスローされます。また、引数のいずれかがnullの場合、NullPointerExceptionがスローされます。
-
- usingEnumValues()メソッドは、enumの値を宣言された順序で含む配列を返すvalues()メソッドの使用方法を示しています。このメソッドは、javaのコンパイラによって自動的に生成されるため、java.util.Enumクラスのvalues()の実装は見つかりません。
-
- usingEnumInSwitch()メソッドは、enum定数をswitchケースで使用する方法を示しています。
-
- usingEnumMap()メソッドは、Java 1.5コレクションフレームワークで導入されたjava.util.EnumMapの使用方法を示しています。EnumMapは、enum型キーを使用するためのMap実装です。Enumマップのすべてのキーは、マップが作成されるときに明示的または暗黙に指定される単一のenumタイプから来る必要があります。EnumMapではキーとしてnullを使用することはできず、EnumMapは同期されていません。
- usingEnumSet()メソッドは、Javaの java.util.EnumSetを使用したSet実装の使用方法を示しています。Enumセットのすべての要素は、セットが作成されるときに明示的または暗黙に指定される単一のenumタイプから来る必要があります。EnumSetは同期されておらず、null要素は許可されません。また、Collection cのcopyOf()メソッド、of(E first、E … rest)メソッド、およびcomplementOf(EnumSet s)メソッドなど、いくつかの便利なメソッドも提供しています。
私たちのGitHubリポジトリから、すべての例をチェックアウトできます。
参照先:オラクルのドキュメント