Effective Java を Kotlin で読む（５）：第6章 enum とアノテーション

![effectivekotlin.png](https://qiita-image-store.s3.amazonaws.com/0/191059/5d874c04-211c-ebca-da5e-5d1a4746517d.png)

# 章目次

[Effective Java を Kotlin で読む（１）：第2章 オブジェクトの生成と消滅](https://qiita.com/ref3000/items/7c896b45c54f1086f0a9) 
[Effective Java を Kotlin で読む（２）：第3章 すべてのオブジェクトに共通のメソッド](https://qiita.com/ref3000/items/0dba04cdcb32b7debdb6)
[Effective Java を Kotlin で読む（３）：第4章 クラスとインタフェース](https://qiita.com/ref3000/items/a6eaf0fb8740ac2aebd3)
[Effective Java を Kotlin で読む（４）：第5章 ジェネリックス](https://qiita.com/ref3000/items/4e0c57f67c905a81fa0a)
[Effective Java を Kotlin で読む（５）：第6章 enum とアノテーション](https://qiita.com/ref3000/items/8ea7008007b8eea315dc) 👈この記事
[Effective Java を Kotlin で読む（６）：第7章 メソッド](https://qiita.com/ref3000/items/9a588356dd8a8879d6a6)
[Effective Java を Kotlin で読む（７）：第8章 プログラミング一般](https://qiita.com/ref3000/items/2d458044fc5a4309de06)
Effective Java を Kotlin で読む（８）：第9章 例外
Effective Java を Kotlin で読む（９）：第10章 並行性
Effective Java を Kotlin で読む（１０）：第11章 シリアライズ

# 第6章 enum とアノテーション

## 項目30 int 定数の代わりに enum を使用する

### 概要
Java にはリリース1.5より列挙型として enum 型 が導入された。それまでは以下のような __int enum パターン__が用いられていた。

```java
public static final int APPLE_FUJI = 0;
public static final int APPLE_PIPPIN = 1;
public static final int APPLE_GRANNY_SMITH = 2;
```

このパターンはいくつかの欠点を持つ。

- 型安全でない
- 名前空間を持たない
- グループをイテレートできない
- etc...

enum 型はこれら欠点を解消する事に加え、任意のメソッドやフィールドを追加でき、任意のインタフェースを実装する事ができる。

メソッドを追加し、定数固有のメソッドを持たせた例が以下である。

```java
enum Hoge0 {
	A { int hoge(int n) { return n * 2; } },
	B { int hoge(int n) { return n * 2; } },
	C { int hoge(int n) { return n * 3; } };

	abstract int hoge(int n);
}
```

ここで、 A と B は同じ処理なので switch を用いて共通処理をまとめようとすると、次のようになる。

```java
enum Hoge1 {
	A, B, C;

	int hoge(int n) {
		switch (this) {
			case A: case B: return n * 2;
			case C: return n * 3;
		}
		return -1; // unreachable
	}
}
```

ただしこの場合、新たな定数を追加した際に忘れず switch に分岐を追加しなければならない等、保守性に難がある。そこで、書籍では __戦略enumパターン__ を紹介している。以下のようになる。

```java
enum Hoge2 {
	A(Strategy.STRATEGY_1), B(Strategy.STRATEGY_1), C(Strategy.STRATEGY_2);
	private final Strategy strategy;
	Hoge2(Strategy strategy) { this.strategy = strategy; }

	int hoge(int n) { return strategy.hoge(n); }

	private enum Strategy {
		STRATEGY_1 { int s(int n) { return n * 2; } }, STRATEGY_2 { int s(int n) { return n * 3; } };
		abstract int s(int n);
		int hoge(int n) { return s(n); }
	}
}
```

switch 文よりも簡潔ではないが、より安全で柔軟である。（個人的にはやりすぎに思う…）

enum 型は int enum パターンの欠点を解消し多くの利点を持つ。パフォーマンスもほぼ int 定数と比べて遜色ない。固定数の定数が必要な場合には、基本的に常に enum 型を利用するべきである。

__追記__
戦略 enum パターンは Java 8 で導入されたメソッド参照・関数インタフェースを用いて以下のように簡潔に書けるようになった！

```java
enum Hoge3 {
	A (Hoge3::strategy1),
	B (Hoge3::strategy1),
	C (Hoge3::strategy2);

	private final Function<Integer, Integer> func;
	Hoge3(Function<Integer, Integer> func) { this.func = func; }

	public int hoge(int n) { return this.func.apply(n); }

	private static int strategy1(int n) { return n * 2; }
	private static int strategy2(int n) { return n * 3; }
}
```

### Kotlin で読む

Kotlin でも列挙型は言語サポートされている。

```kotlin
enum class Direction {
  NORTH, SOUTH, WEST, EAST
}

// こう使う
Direction.NORTH.name // "NORTH"
Direction.SOUTH.ordinal // 1
Direction.values() // {NORTH, SOUTH, WEST, EAST}
```

Java とほぼ同じだが `name()` と `ordinal()` がプロパティになっているのが変更点だろうか…

Java のコードを Kotlin に変換する際に `getName()` のようなメソッドを Java で定義していた場合は少し気をつける必要があるかもしれない。

また、Kotlin の enum は when 式と相性が良い。Java の例で出したクラスを Kotlin で書くと次のようになるだろう。

```kotlin
enum class Hoge {
	A, B, C;
	fun hoge(n: Int): Int = when(this) {
		A, B -> n * 2
		C -> n * 3
	}
}
```

[Try Kotlin で確認](https://try.kotlinlang.org/#/UserProjects/mh5r5j9l413hdu0b9ms1abu140/77rmjh304lfi8imsbq1a165rge)

when は Java でいうところの switch であるが、[すべての分岐をカバーする必要がある（網羅できていない場合 else 条件が必要）](https://kotlinlang.org/docs/reference/control-flow.html#when-expression)。そのため、新たな定数を追加した場合 when に分岐を追加しなければコンパイルエラーとなる。よって、戦略 enum パターンを Kotlin で使う必要性は無いと思われる。

__追記__
ラムダ式で書くとさらにスマート！

```kotlin
private fun strategy1(n: Int) = n * 2
private fun strategy2(n: Int) = n * 3
enum class Fuga(val fuga: (Int) -> Int) {
	A(::strategy1),
	B(::strategy1),
	C(::strategy2);
}
```

[Try Kotlin で確認](https://try.kotlinlang.org/#/UserProjects/mh5r5j9l413hdu0b9ms1abu140/77rmjh304lfi8imsbq1a165rge)

## 項目31 序数の代わりにインスタンスフィールドを使用する

### 概要

enum 型は ordinal メソッドを持っている。これは列挙宣言での各 enum 定数の位置を返すものであるが、これは EnumSet や EnumMap などの汎用の enum に基づくデータ構造のために設計されているものであり、通常利用すべきではない。

何故なら、以下のような性質があるためである。

- 並べ替えで順序が変わる
- 同じ値を割り振れない
- 値を飛ばすことができない

よって、enum に関連付ける値がある場合は、インスタンスフィールドを用いるべきである。

```java
public enum Ensemble {
	SOLO(1), DUET(2), TRIO(3), QUARTET(4);
	
	private final int numberOfMusicians;
	Ensemble(int size) { this.numberOfMusicians = size; }
	public int numberOfMusicians() { return numberOfMusicians; }
}
```

### Kotlin で読む

Kotlin でも考えは同様。インスタンスフィールドの初期化が Java よりもすっきり書けて気持ち良い。

```kotlin
enum class Ensemble(val numberOfMusicians: Int) {
	SOLO(1), DUET(2), TRIO(3), QUARTET(4);
}

// こう使う
Ensemble.TRIO.numberOfMusicians // 3
```

[Try Kotlin で確認](https://try.kotlinlang.org/#/UserProjects/mh5r5j9l413hdu0b9ms1abu140/eqaldrggd5fcuotfk4j72jhnlt)

## 項目32 ビットフィールドの代わりに EnumSet を使用する

### 概要

集合の要素として列挙型を利用する場合、従来は各定数に異なる2の累乗を割り当てる int enum パターンが使われてきた。（ビット演算を利用し効率的に操作するため）

```Java
public class Text {
    public static final int STYLE_BOLD      = 1 << 0; // 1
    public static final int SYTLE_ITALIC    = 1 << 1; // 2
    public static final int STYLE_UNDERLINE = 1 << 2; // 4

    // 例： styles = 5 ならば、 STYLE_BOLD かつ STYLE_UNDERLINE
    public void applyStyles(int styles) { ... }
}
```

しかし項目30の通り int enum パターンは多くの短所を持っている。代わりに [EnumSet](https://docs.oracle.com/javase/jp/8/docs/api/java/util/EnumSet.html) を利用するべきである。

EnumSet は Set インタフェースを実装し、かつ内部的にはビット演算を利用している。そのため効率的に集合操作を行うことができる。（唯一の短所は不変な EnumSet を生成できない事）

一応 `Collections.unmodifiableSet(myEnumSet);` のようにして不変にすることはできるが、簡潔性とパフォーマンスが損なわれる。

### Kotlin で読む

Kotlin でも同様。EnumSet を使おう。

また Kotlin であれば不変な EnumSet も簡単に利用可能である。

```kotlin
enum class Style { BOLD, ITALIC, UNDERLINE }

// Set を受け取るようにすることで柔軟性が増す
fun applyStyle(styles: Set<Style>) { println(styles) }

fun main(args: Array<String>) {
	// Set として宣言することで不変にする
	val set: Set<Style> = EnumSet.of(Style.BOLD, Style.UNDERLINE)
	// EnumSet は様々なファクトリーメソッドを持っている
	applyStyle(set)
}

```

[Try Kotlin で確認](https://try.kotlinlang.org/#/UserProjects/mh5r5j9l413hdu0b9ms1abu140/2igp0qrppob60r1t9peej5pmen)

## 項目33 序数インデックスの代わりに EnumMap を使用する

### 概要

enum の種類でデータをまとめたい場合、項目31の通り `ordinal` を使ってはならない。

```java
public class Herb {
    public enum Type {ANNUAL, PERNNIAL, BIENNIAL}
    private final Type type;
    private final String name;
    Herb(Type type, String name) { this.name = name; this.type = type; }
}

Herb[] garden = { ... }; // 様々なハーブ

Set<Herb>[] herbsByType = (Set<Herb>[]) new Set[Herb.Type.values().length];
for (int i = 0; i < herbsByType.length; i++) herbsByType[i] = new HashSet<Herb>();
for (Herb h : garden) herbsByType[h.type.ordinal()].add(h);
// etc: herbsByType[0] -> ANNUAL（1年生植物）なハーブのSet
```

代わりに [EnumMap](https://docs.oracle.com/javase/jp/8/docs/api/java/util/EnumMap.html) を使うべきである。

### Kotlin で読む

一応 Kotlin も Java 同様 EnumMap を使うべきではあるが…

```kotlin
class Herb(val type: Type, val name: String) {
	enum class Type {ANNUAL, PERNNIAL, BIENNIAL}
	override fun toString() = name
}

val garden = arrayOf(
	Herb(Herb.Type.ANNUAL, "a1"),
	Herb(Herb.Type.ANNUAL, "a2"),
	Herb(Herb.Type.BIENNIAL, "b1"),
	Herb(Herb.Type.BIENNIAL, "b2")
) // 様々なハーブ

fun main(args: Array<String>) {
	// Java の例と同じ処理, 出力: EnumMap<Herb.Type, MutableSet<Herb>>
	val map1 = EnumMap<Herb.Type, MutableSet<Herb>>(Herb.Type::class.java)
	Herb.Type.values().forEach { map1[it] = mutableSetOf() }
	garden.forEach { map1[it.type]?.add(it) }
	println(map1)
	
	// groupBy を使う, 出力: Map<Herb.Type, List<Herb>>
	val map2 = garden.groupBy { it.type }
	println(map2)

	// ※追記※
	// groupByTo を使う, 出力: EnumMap<Herb.Type, MutableList<Herb>>
	val map3 = EnumMap<Herb.Type, MutableList<Herb>>(Herb.Type::class.java)
	garden.groupByTo(map3){ it.type }
	println(map3)
}
```

[Try Kotlin で確認](https://try.kotlinlang.org/#/UserProjects/mh5r5j9l413hdu0b9ms1abu140/k31jdq0ngld51cvaut7fl3jhdn)

ハッシュ計算分くらいしかパフォーマンスに差は無さそうだし、この場合は素直に `Iterable.groupBy` で `Map<Herb.Type, List<Herb>>` として分類しても良さそう（あるいは groupByTo を使う）。 EnumMap の Kotlin でのスマートな使い方があれば知りたい。

## 項目34 拡張可能な enum をインタフェースで模倣する

### 概要

Java において enum 型は拡張する事ができない。ほとんどの場合 enum の拡張は間違った考えであるが、ときに拡張可能な enum を使いたい時がある。その際には、インタフェースを利用して擬似的に enum を拡張する事ができる。

```java
interface Operation {
	int apply(int x, int y);
}

enum BasicOperation implements Operation {
	PLUS("+") {
		public int apply(int x, int y) { return x + y; }
	},
	MINUS("-") {
		public int apply(int x, int y) { return x - y; }
	};
	private final String symbol;
	BasicOperation(String symbol) { this.symbol = symbol; }
	@Override public String toString() { return symbol; }
}

enum ExtendedOperation implements Operation {
	TIMES("*") {
		public int apply(int x, int y) { return x * y; }
	};
	private final String symbol;
	ExtendedOperation(String symbol) { this.symbol = symbol; }
	@Override public String toString() { return symbol; }
}

// こう使う
Operation[] ops = {BasicOperation.PLUS, BasicOperation.MINUS, ExtendedOperation.TIMES};
for (Operation op : ops) {
	System.out.printf("3 %s 2 = %d\n", op, op.apply(3, 2));
}
```

ただし、実装は継承できないため、コードが重複してしまう問題はある。もしコードが複雑になるようなら、共通処理をヘルパークラスに抽出する等の工夫が必要である。

### Kotlin で読む

Kotlin も Java と同様にインタフェースを利用して擬似的に enum を拡張する。

```kotlin
interface Operation {
	fun apply(x: Int, y: Int): Int
}

enum class BasicOperation(private val symbol: String) : Operation {
	PLUS("+") { override fun apply(x: Int, y: Int) = x + y },
	MINUS("-") { override fun apply(x: Int, y: Int) = x - y };

	override fun toString() = symbol
}

enum class ExtendedOperation(private val symbol: String) : Operation {
	TIMES("*") { override fun apply(x: Int, y: Int) = x * y };

	override fun toString() = symbol
}

// こう使う
val ops = listOf<Operation>(BasicOperation.PLUS, BasicOperation.MINUS, ExtendedOperation.TIMES)
ops.forEach {
	println("3 $it 2 = ${it.apply(3, 2)}")
}
```

[Try Kotlin で確認](https://try.kotlinlang.org/#/UserProjects/mh5r5j9l413hdu0b9ms1abu140/qeu6630tdskm17uala5qfn18es)

## 項目35 命名パターンよりアノテーションを選ぶ

### 概要

Java では JDK1.5 からアノテーションが導入された。
それ以前は、ソースコードにメタデータを持たせたい場合は命名パターンが使われていた。
（例：JUnit では、テスト対象は名前が `test` で始まるメソッドであった）

が、この命名パターンは大きな短所を持っている。

- 誤字を検知できない
    - 例： `tsetHogeMethod` という名前のメソッドはテストすべきか？
- メタデータの使用箇所を制限できない
    - 例： `testHogeClass` という名前のクラスはテストすべきか？

書籍では、アノテーションの利用できる現在において__命名パターンを使用するのは論外である__と断言している。

### Kotlin で読む

Kotlin も勿論[アノテーション](https://kotlinlang.org/docs/reference/annotations.html)を利用する。
特に Java からの変更点があるわけではないが、Kotlin のプロパティやコンストラクタパラメータからは対応する Java コードが複数あるので、[どの要素に対してアノテーションをつけるか指定する事ができる](https://kotlinlang.org/docs/reference/annotations.html#annotation-use-site-targets)。

```kotlin
class Example(@field:Ann val foo,    // field に対するアノテーション
              @get:Ann val bar,      // getter メソッドに対するアノテーション
              @param:Ann val quux)   // コンストラクタパラメータに対するアノテーション
```

また Kotlin では生の配列が使えなくなった関係で、アノテーションのパラメータに配列を指定する際少し記述が冗長だった。が、これは Kotlin 1.2 より [array literal syntax が使えるようになり解決した](https://kotlinlang.org/docs/reference/annotations.html#arrays-as-annotation-parameters)。

```kotlin
// Kotlin 1.2+:
@Ann(names = ["abc", "foo", "bar"]) 
class Hoge

// Older Kotlin versions:
@Ann(names = arrayOf("abc", "foo", "bar")) 
class Fuga
```

## 項目36 常に Override アノテーションを使用する

### 概要

JDK1.5でいくつかのアノテーション型がライブラリに追加された。その中で一般的なプログラマにとって最も重要なのが `@Override` である。このアノテーションはメソッドに対してのみ利用でき、このアノテーションが付けられたメソッド宣言はスーパータイプの宣言をオーバーライドしていることを示す。

例えば、以下のクラスにはバグが存在する。

```java
public class Bigram {
	public char first, second;
	Bigram(char first, char second) {
		this.first = first;
		this.second = second;
	}
	public boolean equals(Bigram b) {
		return b.first == first && b.second == second;
	}
	public int hashCode() {
		return 31 * first + second;
	}
}

// こう使う
Set<Bigram> set = new HashSet<>();
set.add(new Bigram('a', 'b'));
set.add(new Bigram('a', 'b'));  // 等価なオブジェクトなのでSetのサイズは変わらず1？
System.out.println(set.size()); // -> 2
```

`Object.equals` をオーバーライドしたつもりのコードであるが、引数が `Object` でないので実際にはオーバーロードしてしまっている。

この場合も `@Override` アノテーションを付けていればコンパイルエラーになるためミスに気がつく事ができる。また、逆に `@Override` アノテーション無しにオーバーライドしている場合にも IDE が警告を出すため、意図しないオーバーライドにも気がつける。

このように、オーバーライドを意図したコードには常に `@Override` アノテーションをつけるべきである。

例外として、抽象メソッドをオーバーライドする際やインタフェースを実装する際には `@Override` アノテーションは任意である。つけなくとも必ず実装しないとコンパイルエラーになるためである（が、付けて害がある訳ではない）。

### Kotlin で読む

Kotlin ではオーバーライドの為には `override` 指定子を用いる。
また、抽象メソッドをオーバーライドする際やインタフェースを実装する際も [Java と異なり `override` をつけることが必須](https://kotlinlang.org/docs/reference/classes.html#overriding-methods)である。

```kotlin
interface InterfaceHoge {
	fun hoge()
}

abstract class AbstractFuga {
	abstract fun fuga()
}

class HogeFuga : InterfaceHoge, AbstractFuga() {
	override fun hoge() = println("hoge")
	override fun fuga() = println("fuga")
}

// こう使う
val hoge: InterfaceHoge = HogeFuga()
val fuga: AbstractFuga = HogeFuga()
hoge.hoge()
fuga.fuga()
```

[Try Kotlin で確認](https://try.kotlinlang.org/#/UserProjects/mh5r5j9l413hdu0b9ms1abu140/iu9k9c6h7mv4al19kg77mr7p7a)

## 項目37 型を定義するためにマーカーインタフェースを使用する

### 概要

マーカーインタフェースとは、メソッド宣言を持たないインタフェースの事である（例： `Serializable`）。これはアノテーション同様コードにメタデータをもたせる事になる。

マーカーインタフェースとアノテーション、どちらを使うべきか？

基本的には、型検査の恩恵を受けられるよう、マーカーインタフェースを使うと良い場合が多いが、以下の場合はアノテーションを利用すべきである。

- クラスやインタフェース以外のプログラム要素に対してメタデータを持たせる場合
    - 当然インタフェースを使用できない要素にはマーカーインタフェースは使えない
- メタデータを拡張する予定がある場合
    - アノテーションであれば、パラメータを追加したりといった拡張が可能
    - 一旦実装された後のインタフェースにメソッドを追加するのは、一般に不可能


### Kotlin で読む

Kotlin でも Java 同様となる。
可能であればマーカーインタフェース、必要に応じてアノテーションを使うと良いだろう。

# おわり

- 前： [Effective Java を Kotlin で読む（４）：第5章 ジェネリックス](https://qiita.com/ref3000/items/4e0c57f67c905a81fa0a)
- 次： [Effective Java を Kotlin で読む（６）：第7章 メソッド](https://qiita.com/ref3000/items/9a588356dd8a8879d6a6)

# 参考資料等

- [Kotlin Reference](https://kotlinlang.org/docs/reference/)
- [Kotlin Language Documentation](https://kotlinlang.org/docs/kotlin-docs.pdf)
- Effective Java 第2版
- Kotlin イン・アクション
- [(Blog)Effective Java 読書会 8 日目 「それ enum で出来るよ」](https://amachang.hatenablog.com/entry/20100225/1267114471)