列挙型を使った処理を共通化する (Java)

Javaで複数の列挙型(enum)に対して同じような処理を書く場合に、どのように共通化するかを考えました。この記事では、列挙型で共通のインターフェースを実装しても対処できないような場合について扱います。例えば、values()のようなstaticメソッドが関わる場合です。対象バージョンはJava SE 6です。(たぶん5でも動きます。)

2010/08/13追記
記事のタイトルを「列挙型を使った処理の共通化」から「列挙型を使った処理を共通化する」に変更しました。(前者だと、「『列挙型を使った処理』の共通化」と「列挙型を使った『処理の共通化』」のように2種類の解釈ができて紛らわしいため。)

通化されていない場合

はじめに共通化されていない場合のコードを示します。main()を呼び出すと、2種類の列挙型(Size、Color)に対して、クラス名とメンバ一覧を出力します。printSizeValues()はSizeに対する出力処理、printColorValues()はColorに対する出力処理です。この2つのメソッドは、扱う型以外は全く同じです。

public class EnumTest1 {
    
    private enum Size {SMALL, MEDIUM, LARGE}
    private enum Color {BLUE, GREEN, RED, YELLOW, WHITE}
    
    public static void main(String[] args) {
        
        printSizeValues();
        printColorValues();
        
    }
    
    private static void printSizeValues() {
        
        System.out.println("[Size]");
        for (Size size : Size.values()) {
            System.out.println(size.name());
        }
        System.out.println();
        
    }
    
    private static void printColorValues() {
        
        System.out.println("[Color]");
        for (Color color : Color.values()) {
            System.out.println(color.name());
        }
        System.out.println();
        
    }

}

実行結果は次のようになります。

[Size]
SMALL
MEDIUM
LARGE

[Color]
BLUE
GREEN
RED
YELLOW
WHITE

Classオブジェクトを使った共通化

上記の「共通化されていない場合」のprintSizeValues()とprintColorValues()を共通化し、printEnumValues()というメソッドを作成しました。このコードの実行結果は、「共通化されていない場合」と全く同じです。

public class EnumTest2 {
    
    private enum Size {SMALL, MEDIUM, LARGE}
    private enum Color {BLUE, GREEN, RED, YELLOW, WHITE}
    
    public static void main(String[] args) {
        
        printEnumValues(Size.class);
        printEnumValues(Color.class);
        
    }
    
    private static void printEnumValues(Class<? extends Enum<?>> enumCls) {
        
        System.out.println("[" + enumCls.getSimpleName() + "]");
        for (Enum<?> e : enumCls.getEnumConstants()) {
            System.out.println(e.name());
        }
        System.out.println();
        
    }

}

補足

  • 共通メソッドの引数で、Classオブジェクトを渡します。(厳密には「Class<? extends Enum<?>>」です。)
  • クラス名の取得には、Class#getSimpleName()を使用しています。
  • Size#values()やColor#values()の代わりに、Class#getEnumConstants()を使用しています。*1
  • 共通メソッドは、他のクラスで定義することも可能です。(当然ですが…)

EnumSetオブジェクトを使った共通化

上記の「Classオブジェクトを使った共通化」とは別の方法で、「共通化されていない場合」の2つのメソッドを共通化しました。このコードの実行結果も、「共通化されていない場合」と全く同じです。

import java.util.EnumSet;

public class EnumTest3 {
    
    private enum Size {SMALL, MEDIUM, LARGE}
    private enum Color {BLUE, GREEN, RED, YELLOW, WHITE}
    
    public static void main(String[] args) {
        
        printEnumValues(Size.class.getSimpleName(), EnumSet.allOf(Size.class));
        printEnumValues(Color.class.getSimpleName(), EnumSet.allOf(Color.class));
        
    }
    
    private static void printEnumValues(String enumName, EnumSet<?> es) {
        
        System.out.println("[" + enumName + "]");
        for (Enum<?> e : es) {
            System.out.println(e.name());
        }
        System.out.println();
        
    }

}

補足

  • 共通メソッドの引数で、EnumSetオブジェクトを渡します。(厳密には「EnumSet<?>」です。)
  • クラス名などのクラス自体の情報は、EnumSetオブジェクトとは別に渡す必要があります。*2
  • 共通メソッドを呼び出す前に、EnumSetオブジェクトを編集することも可能です。(特定の値の追加・削除など)

*1:ちなみに、values()は個別のEnumクラスに対してコンパイラが自動生成するstaticメソッドです。

*2:もちろん、「Classオブジェクトを使った共通化」のようにClassクラスのオブジェクトを渡しても構いません。