👋

列挙型まとめ[Java入門]

に公開

はじめに

こんにちは。
プログラミング初心者wakinozaと申します。
Java勉強中に調べたことを記事にまとめています。

十分気をつけて執筆していますが、なにぶん初心者が書いた記事なので、理解が浅い点などあるかと思います。
間違い等あれば、指摘いただけると助かります。

記事を参考にされる方は、初心者の記事であることを念頭において、お読みいただけると幸いです。

対象読者

  • Javaを勉強中の方
  • Java SE11 Gold試験を勉強中の方
  • Javaの列挙型について知りたい方

目次

1. 列挙型の導入以前
2. 列挙型の特徴
3. 列挙型のメソッド

本文

1. 列挙型の導入以前

列挙型(Enum)は、定数をまとめて定義するための記述方法です。

列挙型導入以前は、プログラム中で多用される定数を定義するためには、インターフェースが使われていました。
以下は、インターフェースにint型の定数を定義したコード例です。

public interface Animals {
  public final static int DOG = 0;
  public final static int CAT = 1;
  public final static int MOUSE = 2;
}

public class Sample {
  int animal = Animals.DOG;
}

この方法は広く利用されていましたが、主に2つの問題がありました。

1つ目は、代入する値をチェックできないという点です。Sampleクラスのフィールドanimalはint型であるため、定数で定義した値(0~2)以外も代入できてしまいます。もし定義した値以外が代入されても、文法上は問題ないため、コンパイルでもチェックできません。
もう一つの問題は、定数の重複です。コードの変更を繰り返すうちに、定数で定義した値が重複してしまう可能性があります。定数の同値判定は、定数名ではなく、定数値で行われるため、異なる定数同士が等値と判定されるという状況も起こり得ます。

public interface Animals {
  public final static int DOG = 0;
  public final static int CAT = 1;
  public final static int MOUSE = 2;
  public final static int BIRD = 0;  //DOGと定数値が重複している
}

public class Sample {
  int animal = Animals.DOG;
  if (animal == Animals.BIRD) {
    System.out.println("DOG equals BIRD ? really?");
  }
}

上のコードをご覧ください。Animalsインターフェースで定数DOGと定数BIRDが同じ定数値が定義されています。定数としては問題がありますが、文法上は問題ないため、コンパイラは通ってしまいます。
また、等値は定数名ではなく、定数値で判定されるため、Animals.DOGが代入された変数animalは、Animals.BIRDと等値と判定されてしまいます。

このような問題を解決するため、定数をまとめて定義するための記述方法が導入されました。
それが列挙型です。
次の節で、列挙型の記述方法を紹介します。

2. 列挙型の特徴

列挙型の宣言方法

列挙型は、以下のように定義します。

アクセス修飾子 enum 列挙型名 {
  列挙子1,列挙子2,列挙子3,....,列挙子N
}

列挙型はクラスやインタフェースと同じように、独立した型として扱われます。
enumキーワードの後に列挙型名を記載し、続く列挙型のブロックに、「列挙子」を記述します。
「列挙子」とは列挙型が定義する個々の値のことです。

コードで書くと以下のようになります。

public enum Hands {
  STONE, SCISSOR, PAPER
}

列挙型は、他のクラスからは1つの型として扱われるため、フィールドやローカル変数の型として使うことができます。
列挙型を活用したクラス例が、以下のとおりです。

public class Player {
  private Hands hand;
}

Playerクラスのフィールドhandは、列挙型のHands型で定義されているため、列挙子以外の値が入ることはありません。
不正な値が入る心配がなく、型安全なクラスとなりました。

列挙型のインスタンス生成

列挙型はクラスの一種として扱われますが、列挙型そのもののインスタンスは生成できません。その代わり、列挙子が1つずつ別のインスタンスとして生成されます。
列挙子のインスタンス生成は、使用されるタイミングで、JVMが自動的に生成します。そのため、開発者がインスタンスを生成する必要はありません。

列挙子の記述と省略方法

列挙子インスタンスを利用する場合は、「列挙型名.列挙子名」という記述します。
具体的に、列挙型HandsのSTONE列挙子を利用する場合は、以下のように記述します。

hand = Hands.STONE; 

列挙型名を省略したい場合は、staticインポートを宣言すると、列挙型名を省略した記述が可能です。
先ほどの、コードでstaticインポートを宣言すると、以下のコードになります。(Handsはjannkennパッケージに属しているとする)

import static jannkenn.Hands.STONE;
// 「STONE」と書かれていたら、「jannkenn.Hands.STONE」だと宣言しておく

hand = STONE; 

列挙子の等値判定

列挙型のインスタンスは単一の実態であることが保証されているため、同一かどうかの判定には「==」演算子を使用することができます。

3. 列挙型のメソッド

開発者が独自に定義した列挙型は、暗黙的にjava.lang.Enumクラスを継承しています。
そのため、Enum クラスが持つ name() や ordinal() といったメソッドを利用できます。

それらに加え、コンパイラが自動的に追加する便利な static メソッドも存在します。
代表的な2つのメソッドを見ていきましょう。

1. values()

public static T[] values()

列挙型で定義された列挙子のインスタンスへの参照を配列として返します。

public enum Letters {
  A, B, C
}

public class Main {
  public static void main (String[] args){
    for (Letters l: Letters.values()){
      System.out.println(l);
    }
  }
}
//結果:
//A
//B
//C

また、インデクスを指定することで、特定の列挙子だけを返すこともできます。

System.out.println(Letters.values()[1]);
//結果:B

2. valueOf()

public static T valueOf(String)

引数の文字列と一致する列挙子のインスタンスへの参照を返します。

System.out.println(Letters.valueOf("A"));
//結果:A

まとめ

  • 列挙型(Enum)は、インターフェースによる定数定義が抱えていた「型安全でない」「値が重複しうる」といった問題を解決するために導入された

  • 列挙型は、定義された列挙子以外の不正な値の代入を防ぐことができる


記事は以上です。
最後までお読みいただき、ありがとうございました。

参考情報一覧

この記事は以下の情報を参考にして執筆しました。

GitHubで編集を提案

Discussion