36.bitフィールドの替わりにEnumSetを用いるべし
enum型の要素が集合に適用される場合、昔はint enumパターン(Item34)を用いて、各定数に2の何乗かを振り分けていた。
// Bit field enumeration constants - OBSOLETE!
public class Text {
public static final int STYLE_BOLD = 1 << 0; // 1
public static final int STYLE_ITALIC = 1 << 1; // 2
public static final int STYLE_UNDERLINE = 1 << 2; // 4
public static final int STYLE_STRIKETHROUGH = 1 << 3; // 8
// Parameter is bitwise OR of zero or more STYLE_ constants
public void applyStyles(int styles) { ... }
}
これらにOR演算をかけることによって、いくつかの要素を集合にまとめていた。(bit field)
text.applyStyles(STYLE_BOLD | STYLE_ITALIC);
bit演算によって、結合や交差も実現できるが、この方法はint enum の悪い点をすべて引き継いでいる。
定数の集合を作るにおいては、EnumSet
クラスを使うべき。性能はbit fieldで処理する場合と遜色ない。
上記の例をenum、EnumSetを用いたのが以下。
// EnumSet - a modern replacement for bit fields
public class Text {
public enum Style { BOLD, ITALIC, UNDERLINE, STRIKETHROUGH }
// Any Set could be passed in, but EnumSet is clearly best
public void applyStyles(Set<Style> styles) { ... }
}
EnumSet
インスタンスを渡す、クライアント側のコードは以下のよう。
text.applyStyles(EnumSet.of(Style.BOLD, Style.ITALIC));
上記のapplyStyles
メソッドでは、EnumSet
ではなく、Set
を引数に取っている。たいていのユーザはEnumSet
を渡すであろうが、引数として実装クラスを受け付けるのではなく、インターフェースを受け付けるべき(Item64)という原則にしたがってこうしている。