概要
Java 1.5より導入されたenumのおさらいメモです。
環境
- Windows 10 Professional
- OpenJDK 11
参考
- [Class Enum] (https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/Enum.html)
- [Class EnumSet] (https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/util/EnumSet.html)
- [Class EnumMap] (https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/util/EnumMap.html)
JDKのおさらい
Enum
サンプル
public enum Fruits {
Apple,
Banana,
Cherry,
Durian,
Elderberry,
Feijoa,
Guava {
@Override
public String toString() {
return name().toLowerCase();
}
};
@Override
public String toString() {
return this.name().toUpperCase();
}
}
name()
enum定数の名前を返します。(Returns the name of this enum constant)
ordinal()
列挙定数の序数(宣言された位置)を返します。(Returns the ordinal of this enumeration constant)
(ほとんどはenum定数(enum constant)と表記されていますが、ordinalのところだけ列挙定数(enumeration constant)と表記されています。)
valueOf(String)
enum定数の名前から対応するenum定数を返します。
valueOf(String)はコンパイラによって合成される暗黙宣言メソッド(public static)です。
values()
enum typeの全ての定数を返します。
values()はコンパイラによって合成される暗黙宣言メソッド(public static)です。
equals()
enum定数同士を比較する場合、通常は==
を使います。
なおEnumクラスのequalsメソッドは下記のようにオーバーライドしています。
public final boolean equals(Object other) {
return this==other;
}
EnumSet
列挙型と一緒に使用するための特殊なSet実装です。enumセット内の要素はすべて、セットの作成時に、指定された単一のenum型から明示的または暗黙的に生成される必要があります。
コンストラクタは無いので次のファクトリメソッドでインスタンスを生成します。
allOf(Class<E>)
指定された列挙型のすべての列挙子を含むEnumSetを作成します。
EnumSet<Fruits> all = EnumSet.allOf(Fruits.class);
System.out.println(all.size());
// → 7
all.forEach(System.out::println);
// → APPLE
// → BANANA
// → CHERRY
// → DURIAN
// → ELDERBERRY
// → FEIJOA
// → guava
noneOf()
指定された列挙型の空のEnumSetを作成します。
EnumSet<Fruits> none = EnumSet.noneOf(Fruits.class);
System.out.println(none.size());
// → 0
none.forEach(System.out::println);
of(E)
指定された列挙子を最初に含むEnumSetを作成します。
EnumSet<Fruits> one = EnumSet.of(Fruits.Durian);
System.out.println(one.size());
// → 1
one.forEach(System.out::println);
// → DURIAN
range(E, E)
指定された2つの列挙子の範囲内のすべての列挙子を含むEnumSetを作成します。
EnumSet<Fruits> range = EnumSet.range(Fruits.Cherry, Fruits.Feijoa);
System.out.println(range.size());
// → 4
range.forEach(System.out::println);
// → CHERRY
// → DURIAN
// → ELDERBERRY
// → FEIJOA
EnumMap
列挙型のキーと一緒に使用するための特殊なMap実装です。enumマップ内のキーはすべて、マップの作成時に、指定された単一のenum型から明示的または暗黙的に生成される必要があります。
EnumSetとは違いコンストラクタを使ってインスタンスを生成します。
EnumMap(Class<k>)
指定した列挙型の空のEnumMapを作成します。
EnumMap<Fruits, String> enumMap = new EnumMap<>(Fruits.class);
enumMap.put(Fruits.Apple, "リンゴ");
enumMap.put(Fruits.Banana, "バナナ");
enumMap.put(Fruits.Cherry, "チェリー");
enumMap.forEach((k, v) -> System.out.println(k + ":" + v));
// → APPLE:リンゴ
// → BANANA:バナナ
// → CHERRY:チェリー
EnumMap(EnumMap<K,? extends V> m)
指定したEnumMapからEnumMapを作成します。
コードは割愛します。
EnumMap(Map<K,? extends V> m)
指定したMapからEnumMapを作成します。
Map<Fruits, String> map = new HashMap<>();
map.put(Fruits.Apple, "リンゴ");
map.put(Fruits.Banana, "バナナ");
map.put(Fruits.Cherry, "チェリー");
EnumMap<Fruits, String> enumMap = new EnumMap<>(map);
enumMap.forEach((k, v) -> System.out.println(k + ":" + v));
// → APPLE:リンゴ
// → BANANA:バナナ
// → CHERRY:チェリー
Enhanced Enums
OpenJDKのProject Amberで検討されているenum拡張([JEP 301: Enhanced Enums] (https://openjdk.java.net/jeps/301))ですが、現在(2019/01)のステータスはOn Hold(保留)ということでまだリリースされていません。
定数固有メソッド実装
enum型で抽象メソッドを宣言し定数固有クラス本体(constant-specific class body)で、enum定数ごとにその抽象メソッドをオーバーライドすることを定数固有メソッド実装と呼びます。(「Effective Java 第2版, 項30 int定数の代わりにenumを使用する」より)
以下は、OSSにみる定数固有メソッド実装(constant-specific method implementation)の例です。
Spring Boot
org.springframework.boot.convert.DurationStyle
public enum DurationStyle {
SIMPLE("^([\\+\\-]?\\d+)([a-zA-Z]{0,2})$") {
@Override
public Duration parse(String value, ChronoUnit unit) {
// ...省略...
}
@Override
public String print(Duration value, ChronoUnit unit) {
// ...省略...
}
},
ISO8601("^[\\+\\-]?P.*$") {
@Override
public Duration parse(String value, ChronoUnit unit) {
//
}
@Override
public String print(Duration value, ChronoUnit unit) {
//
}
}
private final Pattern pattern;
DurationStyle(String pattern) {
this.pattern = Pattern.compile(pattern);
}
public abstract Duration parse(String value, ChronoUnit unit);
public abstract String print(Duration value, ChronoUnit unit);
}
Hibernate-ORM
org.hibernate.envers.query.criteria.MatchMode
public enum MatchMode {
EXACT {
@Override
public String toMatchString(String pattern) {
return pattern;
}
},
START {
@Override
public String toMatchString(String pattern) {
return pattern + '%';
}
},
// ...省略...
public abstract String toMatchString(String pattern);
}
org.hibernate.dialect.Database
public enum Database {
CACHE {
// ...省略...
},
// ...省略...
MYSQL {
@Override
public Class<? extends Dialect> latestDialect() {
return MySQL57Dialect.class;
}
@Override
public Dialect resolveDialect(DialectResolutionInfo info) {
final String databaseName = info.getDatabaseName();
if ( "MySQL".equals( databaseName ) ) {
final int majorVersion = info.getDatabaseMajorVersion();
final int minorVersion = info.getDatabaseMinorVersion();
if ( majorVersion < 5 ) {
return new MySQLDialect();
}
else if ( majorVersion == 5 ) {
if ( minorVersion < 5 ) {
return new MySQL5Dialect();
}
else if ( minorVersion < 7 ) {
return new MySQL55Dialect();
}
else {
return new MySQL57Dialect();
}
}
return latestDialectInstance( this );
}
return null;
}
},
ORACLE {
// ...省略...
}
// ...省略...
public abstract Class<? extends Dialect> latestDialect();
public abstract Dialect resolveDialect(DialectResolutionInfo info);
}
elasticsearch
org.elasticsearch.common.unit.SizeUnit
public enum SizeUnit {
SINGLE {
@Override
public long toSingles(long size) {
return size;
}
@Override
public long toKilo(long size) {
return size / (C1 / C0);
}
@Override
public long toMega(long size) {
return size / (C2 / C0);
}
@Override
public long toGiga(long size) {
return size / (C3 / C0);
}
@Override
public long toTera(long size) {
return size / (C4 / C0);
}
@Override
public long toPeta(long size) {
return size / (C5 / C0);
}
},
KILO {
// ...省略...
},
MEGA {
// ...省略...
},
// ...省略...
public abstract long toSingles(long size);
public abstract long toKilo(long size);
public abstract long toMega(long size);
public abstract long toGiga(long size);
public abstract long toTera(long size);
public abstract long toPeta(long size);
}
org.elasticsearch.xpack.sql.expression.function.scalar.string.BinaryStringNumericProcessor
public class BinaryStringNumericProcessor extends FunctionalBinaryProcessor<String, Number, String, BinaryStringNumericOperation> {
public enum BinaryStringNumericOperation implements BiFunction<String, Number, String> {
LEFT((s,c) -> {
int i = c.intValue();
if (i < 0) {
return "";
}
return i > s.length() ? s : s.substring(0, i);
}),
RIGHT((s,c) -> {
// ...省略...
}),
REPEAT((s,c) -> {
// ...省略...
});
BinaryStringNumericOperation(BiFunction<String, Number, String> op) {
this.op = op;
}
private final BiFunction<String, Number, String> op;
@Override
public String apply(String stringExp, Number count) {
if (stringExp == null || count == null) {
return null;
}
return op.apply(stringExp, count);
}
}
// ...省略...
}
logback
ch.qos.logback.core.joran.spi.ConsoleTarget
public enum ConsoleTarget {
SystemOut("System.out", new OutputStream() {
@Override
public void write(int b) throws IOException {
System.out.write(b);
}
@Override
public void write(byte b[]) throws IOException {
System.out.write(b);
}
@Override
public void write(byte b[], int off, int len) throws IOException {
System.out.write(b, off, len);
}
@Override
public void flush() throws IOException {
System.out.flush();
}
}),
SystemErr("System.err", new OutputStream() {
// ...省略...
});
private final String name;
private final OutputStream stream;
private ConsoleTarget(String name, OutputStream stream) {
this.name = name;
this.stream = stream;
}
// ...省略...
}