2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

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

Posted at

はじめに

  • 昨年、私が進行していたJavaスタディで整理した内容です。

  • 内容に不正確な点がございましたら、ご指摘いただけますと幸いです。
    古い内容を編集してアップロードしたため、十分に検証されていない部分や不自然な箇所があるかもしれません。
    (今後も継続的に修正してまいります)

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

Enum の定義方法

public enum Company {
    APPLE,
    SAMSUNG,
    GOOGLE,
    MICROSOFT
}
  • 各項目は ,(カンマ)で区切ります
  • 最後の項目の後にある ; は任意です
  • すべての文字は大文字で記述します(慣例)
  • 複数の単語で構成される場合は、_(アンダースコア)で区切ります
Company company = Company.APPLE;

このように使用します。

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

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

  • Enum のデフォルトコンストラクタは常に private です(コンパイラによって強制的に private に設定されます)。
  • Enum はフィールドを持つことができます。
  • 各定数はコンストラクタを通じて対応するフィールドを初期化することができます。
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 に一般的なメソッドを定義することができます
  • すべての定数で共通して使用するメソッドを定義したり、特定の定数でのみオーバーライドするように設定することもできます
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 内部に静的メソッドを定義することで、列挙型全体に関連する動作を処理することができます
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 以前)

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

問題点

  1. 型の安全性が不足している
    • 他の定数グループと比較が可能になってしまう
if (Company.APPLE == DayOfWeek.MONDAY) // コンパイルエラーが発生しない
  • 名前空間の欠如
    1. すべての定数名が一意でなければならない
    2. 他の定数グループで同じ名前を使用することができない

Type-saftey

public enum Company {
    APPLE, SAMSUNG, GOOGLE
}

public enum DayOfWeek {
    MONDAY, TUESDAY, WEDNESDAY
}

// コンパイルエラーが発生
if (Company.APPLE == DayOfWeek.MONDAY)
  • 型の安全性が保証される
  • JVM 上でインスタンスは1つだけ存在
    • シングルトンが保証される

Enum が提供するメソッド

name()

  • 列挙型定数の名前を返します

    enum Color {
        RED, GREEN, BLUE
    }
    
    public class Main {
        public static void main(String[] args) {
            System.out.println(Color.RED.name()); // "RED" と出力されます
        }
    }
    

    image.png

ordinal()

  • 列挙型定数の宣言順序を返します (0からNまで)

    enum Color {
        RED, GREEN, BLUE
    }
    
    public class Main {
        public static void main(String[] args) {
            System.out.println(Color.GREEN.ordinal()); // 1 と出力
        }
    }
    

    image.png

toString()

  • 列挙型定数の名前を文字列で返します
  • EnumはObjectを継承するため、toStringを継承します
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

EnumはObjectクラスを継承します

image.png
バイトコードを確認すると、以下のようにObjectのコンストラクタが呼び出されていることが分かります

equals(Object other)

  • 二つの列挙型定数が同じかどうかを比較します
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

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

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

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

Enumと型安全性

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

  • さらにジェネリクスに関連して、この利点があります
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)

  • 列挙型定数間の順序を比較します
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

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

getDeclaringClass()

  • 列挙型定数が属する列挙型クラスのオブジェクトを返します
enum Color {
    RED, GREEN, BLUE
}

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

image.png

valueOf(Class<T> enumClass, String name)

  • 文字列の名前を用いて、特定の列挙型定数を返します
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

  • 列挙型定数の名前が完全に一致する必要があります

describeConstanble()

  • 列挙型定数のEnumEscを包んだOptionalオブジェクトを返します
  • Java 12以降で使用可能です
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

EnumDesc

  • EnumDescはJava 12から導入されたクラスです

  • 列挙型(Enum)定数に対する「名目的 (nominal) 説明子」を表現するために使用されます

  • 型安全性および有意義な情報を提供します

    image.png

clone()

image.png

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

readObject(ObjectInputStream in)

  • シリアライズおよびデシリアライズの過程で常にシングルトンが維持されます
  • つまり、標準のシリアライズは使用できず、例外が投げられます

image.png

  • Javaのシングルトンおよびenumシングルトンに関する情報は、こちらにまとめました
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

finalize()

image.png

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

java.lang.Enum

image.png

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

Enum.set

image.png

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

image.png

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

image.png
image.png

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

Bit VectorとEnumSet

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

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
    }
}
    

ビット演算を活用したEnum定数の管理

image.png
image.png

  • 各Enum定数は、そのordinal()の値に対応するビットで表現されます

ENUM Set 使用例

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

その他のEnumのすべてのメソッド

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

2
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?