JavaのEnumについて整理してみました


# はじめに
- 昨年、私が進行していたJavaスタディで整理した内容です。  
- 内容に不正確な点がございましたら、ご指摘いただけますと幸いです。  
古い内容を編集してアップロードしたため、十分に検証されていない部分や不自然な箇所があるかもしれません。  
(今後も継続的に修正してまいります)

- 翻訳機の助けを借りて作成した文ですので、誤りがある可能性がございます

# Enum の定義方法

```java
public enum Company {
    APPLE,
    SAMSUNG,
    GOOGLE,
    MICROSOFT
}
```

- 各項目は ,（カンマ）で区切ります
- 最後の項目の後にある ; は任意です
- すべての文字は大文字で記述します（慣例）
- 複数の単語で構成される場合は、_（アンダースコア）で区切ります

```java
Company company = Company.APPLE;
```

このように使用します。

- 列挙型の定数は `static final` として扱われます

## コンストラクタとフィールドの定義

- Enum のデフォルトコンストラクタは常に `private` です（コンパイラによって強制的に `private` に設定されます）。  
- Enum はフィールドを持つことができます。  
- 各定数はコンストラクタを通じて対応するフィールドを初期化することができます。  


```java
enum Color {
    RED("赤色"), GREEN("緑色"), BLUE("青色");

    private final String description; // フィールドの定義

    // コンストラクタの定義
    private Color(String description) {
        this.description = description;
    }

    // フィールドの getter メソッド
    public String getDescription() {
        return description;
    }
}

public class Main {
    public static void main(String[] args) {
        System.out.println(Color.RED.getDescription()); // "赤色" を出力
        System.out.println(Color.GREEN.getDescription()); // "緑色" を出力
    }
}
```

## メソッドの定義

- Enum に一般的なメソッドを定義することができます
- すべての定数で共通して使用するメソッドを定義したり、特定の定数でのみオーバーライドするように設定することもできます 

```java
enum Operation {
    ADD {
        @Override
        public int apply(int x, int y) {
            return x + y;
        }
    },
    SUBTRACT {
        @Override
        public int apply(int x, int y) {
            return x - y;
        }
    },
    MULTIPLY {
        @Override
        public int apply(int x, int y) {
            return x * y;
        }
    };

    // 抽象メソッドの宣言（定数ごとに実装）
    public abstract int apply(int x, int y);
}

public class Main {
    public static void main(String[] args) {
        System.out.println(Operation.ADD.apply(3, 4)); // 7
        System.out.println(Operation.SUBTRACT.apply(10, 5)); // 5
        System.out.println(Operation.MULTIPLY.apply(2, 3)); // 6
    }
}
```

## 静的メソッドの定義

- Enum 内部に静的メソッドを定義することで、列挙型全体に関連する動作を処理することができます

```java
enum Color {
    RED, GREEN, BLUE;

    public static Color fromString(String colorName) {
        return switch (colorName.toUpperCase()) {
            case "RED" -> RED;
            case "GREEN" -> GREEN;
            case "BLUE" -> BLUE;
            default -> throw new IllegalArgumentException("不明な色: " + colorName);
        };
    }
}

public class Main {
    public static void main(String[] args) {
        Color color = Color.fromString("red");
        System.out.println(color); // RED を出力
    }
}

```

## 継承

Enum クラスは継承できず、列挙型はサブクラスでオーバーライドすることもできません。

（暗黙的にコンパイラによって `final` として処理されます）


# Enum はなぜ使うのか

### int Enum パターン (Java 5 以前)

```java
public class Company {
    public static final int APPLE = 1;
    public static final int SAMSUNG = 2;
    public static final int GOOGLE = 3;
}
```

### 問題点

1. 型の安全性が不足している  
    -  他の定数グループと比較が可能になってしまう  

```java
if (Company.APPLE == DayOfWeek.MONDAY) // コンパイルエラーが発生しない
```

- 名前空間の欠如  
    1. すべての定数名が一意でなければならない  
    2. 他の定数グループで同じ名前を使用することができない  


## Type-saftey

```java
public enum Company {
    APPLE, SAMSUNG, GOOGLE
}

public enum DayOfWeek {
    MONDAY, TUESDAY, WEDNESDAY
}

// コンパイルエラーが発生
if (Company.APPLE == DayOfWeek.MONDAY)
```

- 型の安全性が保証される
- JVM 上でインスタンスは1つだけ存在
    - シングルトンが保証される

# Enum が提供するメソッド

### `name()`

- 列挙型定数の名前を返します
    
    ```java
    enum Color {
        RED, GREEN, BLUE
    }
    
    public class Main {
        public static void main(String[] args) {
            System.out.println(Color.RED.name()); // "RED" と出力されます
        }
    }
    ```

    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3815412/23bae22f-ce7f-4aa7-9e2c-f5cb9b81ca84.png)

    
### `ordinal()`
- 列挙型定数の宣言順序を返します (0からNまで)
    
    ```java
    enum Color {
        RED, GREEN, BLUE
    }
    
    public class Main {
        public static void main(String[] args) {
            System.out.println(Color.GREEN.ordinal()); // 1 と出力
        }
    }
    ```
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3815412/0cc376c0-4e7e-4628-a1f4-2bfe17f9824b.png)


### `toString()`

- 列挙型定数の名前を文字列で返します
- EnumはObjectを継承するため、toStringを継承します

```java
enum Color {
    RED("赤"), GREEN("緑"), BLUE("青");

    private final String japaneseName;

    Color(String japaneseName) {
        this.japaneseName = japaneseName;
    }

    @Override
    public String toString() {
        return this.japaneseName;
    }
}

public class Main {
    public static void main(String[] args) {
        System.out.println(Color.BLUE); // "青"
    }
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3815412/eec3971e-f1c5-400b-9343-a7290e85e2ba.png)

:::note info
### EnumはObjectクラスを継承します
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3815412/53be12e3-efec-479f-aae8-fbb61e63812b.png)
バイトコードを確認すると、以下のようにObjectのコンストラクタが呼び出されていることが分かります
:::


### `equals(Object other)`
- 二つの列挙型定数が同じかどうかを比較します

```java
enum Color {
    RED, GREEN, BLUE
}

public class Main {
    public static void main(String[] args) {
			Color color1 = Color.RED;
      Color color2 = Color.RED;
      System.out.println(color1.equals(color2) // true
    }
}
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3815412/25a7d0e9-8a96-409f-b71d-8b33c1be3b1f.png)


→ シングルトンですが、`==`で比較しても真となるでしょうか？

```java
enum Color {
    RED, GREEN, BLUE
}

public class Main {
    public static void main(String[] args) {
			Color color1 = Color.RED;
      Color color2 = Color.RED;
      System.out.println(color1 == color2) // true
    }
}
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3815412/a398b107-b4c3-40df-8c91-df333639b202.png)


- その通り、trueが返ります。しかし、==を使用しない理由は型安全性が不足しているためです


:::note info
### Enumと型安全性
```java
enum Color {
    RED, GREEN, BLUE
}

enum Stock {
    APPLE, GOOGLE
}

public class Main {
    public static void main(String[] args) {
        Color color1 = Color.RED;
        Stock stock1 = Stock.APPLE;
        System.out.println(color1 == stock1); // コンパイル時に判断できない
        System.out.println(color1.equals(stock1)); // コンパイル時にエラーが発生する
    }
}
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3815412/8b76f292-10eb-477e-b62d-44ee6cd800d9.png)

- さらにジェネリクスに関連して、この利点があります

```java
EnumSet<Color> colors = EnumSet.of(Color.RED, Color.BLUE);
colors.contains(Color.GREEN); // OK - 同じEnum型
colors.contains(DayOfWeek.MONDAY); // コンパイルエラー - 異なるEnum型

List<Color> colorList = new ArrayList<>();
colorList.add(Color.RED);
colorList.contains(Color.GREEN); // OK - 同じEnum型
colorList.contains(DayOfWeek.MONDAY); // コンパイルエラーにはなりませんが、falseが返されます (equals()比較でfalse)
```
:::


### `hashcode()`
- ハッシュコードを返します
### `compareTo(E o)`
- 列挙型定数間の順序を比較します

```java
enum Color {
    RED, GREEN, BLUE
}

public class Main {
    public static void main(String[] args) {
        System.out.println(Color.RED.compareTo(Color.BLUE)); // 負の値が出力されます (REDがBLUEより前にあるため)
    }
}
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3815412/a144c794-f811-429c-ba3b-537119493ba8.png)

- 宣言された順序に従って比較します

### getDeclaringClass()
- 列挙型定数が属する列挙型クラスのオブジェクトを返します

```java
enum Color {
    RED, GREEN, BLUE
}

public class Main {
    public static void main(String[] args) {
        System.out.println(Color.RED.getDeclaringClass()); // class Color が出力
    }
}

```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3815412/cc2e4222-6b8b-42f1-98a4-5befa1014a50.png)


### `valueOf(Class<T> enumClass, String name)`
- 文字列の名前を用いて、特定の列挙型定数を返します

```java
enum Color {
    RED, GREEN, BLUE
}

public class Main {
    public static void main(String[] args) {
        System.out.println(Enum.valueOf(Color.class, "GREEN")); // "GREEN"
    }
}
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3815412/b1a13d82-610f-4a26-b192-1dcde3e9530c.png)
- 列挙型定数の名前が完全に一致する必要があります

### `describeConstanble()`
- 列挙型定数のEnumEscを包んだOptionalオブジェクトを返します
- Java 12以降で使用可能です

```java
enum Color {
    RED, GREEN, BLUE
}

public class Main {
    public static void main(String[] args) {
        System.out.println(Color.RED.describeConstable()); // Optional[EnumDesc[Color.RED]] が出力
    }
}

```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3815412/d489bf1b-3653-4517-9d8d-baaa80675374.png)


### `EnumDesc`
- EnumDescはJava 12から導入されたクラスです
- 列挙型（Enum）定数に対する「名目的 (nominal) 説明子」を表現するために使用されます
- 型安全性および有意義な情報を提供します

    
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3815412/9b735ba4-f0f3-4907-8616-be08926bb521.png)

    
### `clone()`
    
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3815412/b146f9e8-4d75-4b67-94dc-37a45a777f32.png)

- enumは絶対にコピーされることはありません
- したがって、シングルトンであることが保証されます

### `readObject(ObjectInputStream in)`
- シリアライズおよびデシリアライズの過程で常にシングルトンが維持されます
- つまり、標準のシリアライズは使用できず、例外が投げられます
    
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3815412/cab2ed0d-5bfc-4d6d-a05c-b502d2bc2df8.png)

https://qiita.com/haroya01/items/d8b9e6aed17f3476ffbe
- Javaのシングルトンおよびenumシングルトンに関する情報は、こちらにまとめました

```java
package com.example.jpystudy;

import java.lang.constant.ClassDesc;
import java.lang.invoke.MethodHandles;

import java.io.*;

enum Color {
    RED, GREEN, BLUE
}

public class Main {
    public static void main(String[] args) throws Exception {
        // シリアライズ
        Color color = Color.RED;
        ByteArrayOutputStream byteOut = new ByteArrayOutputStream();
        ObjectOutputStream out = new ObjectOutputStream(byteOut);
        out.writeObject(color);

        // デシリアライズ
        ByteArrayInputStream byteIn = new ByteArrayInputStream(byteOut.toByteArray());
        ObjectInputStream in = new ObjectInputStream(byteIn);
        Color deserializedColor = (Color) in.readObject();

        // 比較
        System.out.println(color == deserializedColor); // true (シングルトン保証)
    }
}

```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3815412/440276b0-41f2-432f-b4c3-c24396b51dbf.png)


### `finalize()`
    
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3815412/0556369a-712b-47d4-b73a-2f65d4ad9d34.png)

- 無効化されています
- なぜなら、GCによって終了されるか、終了処理を行う理由がないためです

# java.lang.Enum

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3815412/ca40f57a-092e-410d-9364-3ca025a26c37.png)


- java.lang.Enum<E> はすべての列挙型の抽象基本クラスです
- 多重継承は不可能です
- すべてのenumはこのクラスを暗黙的に継承し、列挙型に関連する基本的な動作やメソッドを提供します
- 以前に述べたように、ENUMはOBJECTを継承しています

# Enum.set

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3815412/a4caff8e-6d5e-4a20-b972-34ae2e8060aa.png)


- Enum型と共に使用するために設計されたSetの実装です
- 列挙型定数を内部的にビットベクトルとして表現することで、非常に効率的な空間および時間性能を提供します


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3815412/4dda340d-928d-4c5e-8a4c-9badac92b0eb.png)

- ビットベクトルは2種類の実装で実現されています 
    - 64個以下: 単一のlong値（64ビット）をビットベクトルとして使用するRegularEnumSet
    - 64個超過: long[]配列を使用するJumboEnumSet

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3815412/90775ba6-5b91-4371-ab37-d6d1cc9ebcb9.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3815412/77b2c123-1e95-458c-b85a-c016f7ef6b53.png)

    
- 一般的なSet実装であるHashSetは、定数オブジェクトとして管理されるため、ビットベクトルを使用するEnumSetよりも多くのメモリを消費します

### Bit VectorとEnumSet

- EnumSetは内部的に上記のビット演算を活用し、列挙型定数を効率的に管理します
- EnumSetを使用することで、型安全にEnumの集合を扱うことができます

```java

enum Color {
        RED, GREEN, BLUE, YELLOW;
    }
    
    public class Main {
    public static void main(String[] args) {
        EnumSet<Color> colors = EnumSet.noneOf(Color.class); // 空のセットを生成

        // REDを追加
        colors.add(Color.RED); // 内部的に bitVector |= RED
        System.out.println(colors); // [RED]

        // GREENを追加
        colors.add(Color.GREEN); // 内部的に bitVector |= GREEN
        System.out.println(colors); // [RED, GREEN]

        // REDを削除
        colors.remove(Color.RED); // 内部的に bitVector &= ~RED
        System.out.println(colors); // [GREEN]

        // 含有判定
        boolean containsBlue = colors.contains(Color.BLUE); // 内部的に bitVector & BLUE
        System.out.println(containsBlue); // false
    }
}
    
```

:::note info
### ビット演算を活用したEnum定数の管理
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3815412/90338958-b90e-4f82-9c16-4bc495da4d47.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3815412/60c5bc09-f697-4186-982e-a419a887ce38.png)

- 各Enum定数は、そのordinal()の値に対応するビットで表現されます
:::
    
### ENUM Set 使用例
```java
enum DayOfWeek {
    MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY
}

public class Main {
    public static void main(String[] args) {
        EnumSet<DayOfWeek> weekdays = EnumSet.of(
                DayOfWeek.MONDAY,
                DayOfWeek.TUESDAY,
                DayOfWeek.WEDNESDAY,
                DayOfWeek.THURSDAY,
                DayOfWeek.FRIDAY
        );

        weekdays.add(DayOfWeek.SATURDAY);
        weekdays.remove(DayOfWeek.MONDAY);

        boolean containsSaturday = weekdays.contains(DayOfWeek.SATURDAY);
        boolean containsMonday = weekdays.contains(DayOfWeek.MONDAY);

        System.out.println(containsSaturday);
        System.out.println(containsMonday);
    }
}
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3815412/8219c91b-f751-4298-9c26-db3e4cdd5a10.png)



# その他のEnumのすべてのメソッド
- その他のすべてのメソッドについては、こちらを参照してください

https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/lang/Enum.html