当メモは2012-05-03に投稿されたものを加筆修正し、再掲したものです。
目次
はじめに
Javaのenumって便利ですよね。enumを使い続けていくとグルーピングしたくなってくる事があると思いますが、継承はできないので、そんな場合はインタフェースを実装してやるといいようです。
インタフェース
インタフェース
interface Colored { String getColor(); }
まず、こんな感じのenumに付けるインタフェースを用意します。Coloredは色付けされたものという意味で、#getColor()
すると、色を文字列で取得できるイメージです。
enum定義
enum定義
enum Food implements Colored { rice { @Override public String getColor() { return "白"; } }, // こめ bread { @Override public String getColor() { return "白と茶"; } }, // ぱん noodle { @Override public String getColor() { return "白"; } }, // めん ; }
enumの定義時にインタフェースを実装してあげます。これは直接実装していってますが、以下のようにプライベートコンストラクタで値を受け取る方法でも可能です。
enum定義 (プライベートコンストラクタ版)
enum Animal implements Colored { cat("白と黒と茶"), // ねこ tiger("黄と黒"), // とら lion("茶"), // らいおん goat("白"), // やぎ sheep("白"), // ひつじ ; private String color; private Animal(String color) { this.color = color; } @Override public String getColor() { return this.color; } }
使ってみる
Colored型で取得
Colored colored1 = Animal.cat; Colored colored2 = Food.rice;
これで、定義したenumはColoredとしても扱えるようになりました。あとはお好きな場面でColored型として利用するだけです。
補足
今回作ったインタフェースのColoredは#getColor()
メソッドしか定義していませんので、enumにもとからあるメソッド(#name()
とか)は当然の事ながら使えません。なので、必要に応じてinterfaceの方に加えておくとよいでしょう。
#nameメソッドの追加
interface Colored { String getColor(); String name(); // ← 追加! }
こうしておけば、enumはもとから#name()
は持っているので、そのままenum側のコードには何も手を加えずに、Coloredとしても#name()
を利用できるようになります。
テストを書いた
EnumTest.java
import static org.junit.Assert.*; import static org.hamcrest.CoreMatchers.*; import java.util.List; import java.util.ArrayList; import org.junit.Test; public class EnumTest { @Test public void enumでインタフェースの実装テスト() { // 用意したenumは 動物 と 食べ物 assertThat(Animal.class.getSimpleName(), is("Animal")); assertThat(Food.class.getSimpleName(), is("Food")); // #values() で全要素が取得可能 assertArrayEquals(new Animal[]{ Animal.cat, Animal.tiger, Animal.lion, Animal.goat, Animal.sheep }, Animal.values()); assertArrayEquals(new Food[]{ Food.rice, Food.bread, Food.noodle }, Food.values()); // さていよいよインタフェースを利用する。 // 動物も食べ物も色がついているものを実装しているので、色がついているものとして扱える for (Colored whiteThing : new Colored[]{ Animal.goat, Animal.sheep, Food.rice }) { assertThat(whiteThing.getColor(), is("白")); } // 茶色いものリストを用意して List<Colored> brownThings = new ArrayList<Colored>(); brownThings.addAll(collectBrown(Animal.values())); brownThings.addAll(collectBrown(Food.values())); // 茶色いものを確認 assertArrayEquals(new Colored[]{ Animal.cat, Animal.lion, Food.bread }, brownThings.toArray(new Colored[brownThings.size()])); } /** * 茶色だけに絞って返却 * @param Coloredな配列 * @return 茶色なColoredのリスト */ private List<Colored> collectBrown(Colored[] coloredList) { List<Colored> result = new ArrayList<Colored>(); for (Colored colored : coloredList) if (colored.getColor().contains("茶")) result.add(colored); return result; } } // ↓ 色付けされたものを表現するインタフェース interface Colored { String getColor(); } // ↓ プライベートコンストラクタでごにょごにょする実装 enum Animal implements Colored { cat("白と黒と茶"), // ねこ tiger("黄と黒"), // とら lion("茶"), // らいおん goat("白"), // やぎ sheep("白"), // ひつじ ; private String color; private Animal(String color) { this.color = color; } @Override public String getColor() { return this.color; } } // ↓ 直接インタフェースを実装してしまってもいい enum Food implements Colored { rice { @Override public String getColor() { return "白"; } }, // こめ bread { @Override public String getColor() { return "白と茶"; } }, // ぱん noodle { @Override public String getColor() { return "白"; } }, // めん ; }