未来エンジニア養成所Blog

月単価180万以上のプログラミング講師がプログラミングを皆に楽しんでもらうための情報をお届けします。

【Java】オブジェクト指向の応用問題3-10

title


問題3-10

四季を表現するSeason列挙型を作成してください。


【Season列挙型】

  1. Season列挙型は4つの列挙子(SPRING、SUMMER、FALL、WINTER)を持ちます。

  2. Season列挙型はprivateなインスタンスフィールドname(String型)を持ち、4つの列挙子に対して「春」「夏」「秋」「冬」という文字列を保持します。
    (コンストラクタをうまく利用してください)

  3. toStringメソッドをオーバーライドして、インスタンスフィールドnameの情報を返してください。
    また、EnumBasicPracticeクラスのmainメソッドは一部未完成です。
    以下のように実装してください。

mainメソッド内で四季列挙子を格納した配列変数を宣言しています。
拡張for文を使ってすべての季節の列挙子をうまく使って表示してください。
ただし、SUMMERの場合は「夏は暑い!!」、WINTERの場合は「冬は寒い!!」という情報を表示するように工夫してください。


【実行結果】

春
夏は暑い!!
秋
冬は寒い!!


【EnumBasicPractice.java】

public class EnumBasicPractice {
    public static void main(String[] args) {

        // 四季列挙子を格納した配列変数の宣言
        Season[] seasons = {
                        Season.SPRING,
                        Season.SUMMER,
                        Season.FALL,
                        Season.WINTER
             };

        // 四季をループで回す


    }
}


// ここに季節列挙型を作成してください


解答例

【EnumBasicPractice.java】

public class EnumBasicPractice {
    public static void main(String[] args) {

        // 四季列挙子を格納した配列変数の宣言
        Season[] seasons = {
                        Season.SPRING,
                        Season.SUMMER,
                        Season.FALL,
                        Season.WINTER
             };

        // 四季をループで回す
        for(Season s : seasons) {
            switch(s) {
                case SUMMER:
                    System.out.println(s + "は暑い!!");
                    break;
                case WINTER:
                    System.out.println(s + "は寒い!!");
                    break;
                case SPRING:
                case FALL:
                    System.out.println(s);
                    break;
                default:
                    // 現状はありえない
                    throw new RuntimeException("Illegal Season!");
            }
        }

    }
}

// 季節列挙型
enum Season {
    SPRING("春"), SUMMER("夏"), FALL("秋"), WINTER("冬");

    // 季節名
    private String name;

    // コンストラクタ
    private Season(final String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return name;
    }
}


解説

「列挙型」はJDK5.0で導入された画期的な技術です。

しかし、あまりそのありがたみが理解されていないように感じます。



JDK1.4以前では、今回のような四季を表現する定数を以下のように定義していました。

// 季節Seasonインタフェース
interface Season {
    public static final int SPRING = 0;
    public static final int SUMMER = 1;
    public static final int FALL = 2;
    public static final int WINTER = 3;
}


しかし、このような記述には致命的な欠点があります。

それは「春は0ではない」ということです。

同様に「夏は1ではない」、さらに「夏+秋=冬ではない」ということです。


昔は定数に型と値が必要だったので、仕方なくint型にして連番を割り振っていたと言うだけです。

こんな作りだと、もし四季の情報を受け取るメソッドを定義するとき、引数がint型なので「0」「1」「2」「3」以外の数値も簡単に渡ってくることになってしまいます。

よって、簡単にバグが混入する可能性が出てくるわけです。



列挙型の発想はこうです。

「春を表す定数SPRINGに値なんていらない」ということ。

そして「SPRING」は「SPRING」と等しく「SUMMER」は「FALL」と等しくない、そういうことをシンプルに表現できるわけです。



列挙型の定義には、キーワード「enum」を使用します。

そして、列挙型の要素を「列挙子(または列挙定数)」といいます。


もし四季を表現する4つの列挙子だけを定義したいなら、次のようになります。

// 季節Season列挙型
enum Season {
    SPRING, SUMMER, FALL, WINTER
}


ちなみに、列挙型に列挙子のみ定義する場合は、最後の列挙子(今回ならWINTER)の後ろにセミコロンは不要です。



そして、列挙型は「特殊なクラス」であり、上記の列挙型は下記のクラスとほぼ同じ意味になります。

// 季節Seasonクラス
class Season {
    public static final Season SPRING = new Season();
    public static final Season SUMMER = new Season();
    public static final Season FALL = new Season();
    public static final Season WINTER = new Season();
    private Season(){};
}


つまり、「列挙子」とは、自分自身のクラスのインスタンスを指す定数なんです。

そして、コンストラクタは必ずprivateと決まっているので、列挙子の偽物は絶対に作れなくなっています。

よって「列挙子」は「オンリーワン」な存在です。



「列挙型」は所詮「特殊なクラス」なので、インスタンスフィールドやインスタンスメソッドの定義、privateコンストラクタのオーバーロード、toStringメソッドのオーバーライドなど、様々なことが可能です。



コンストラクタに引数を渡すときは、列挙子の後ろに丸括弧を記述してデータを渡します。

次のような感じです。

// 季節Season列挙型
enum Season {
    SPRING("春"), SUMMER("夏"), FALL("秋"), WINTER("冬");

    // 季節名
    private String name;

    // コンストラクタ
    private Season(final String name) {
        this.name = name;
    }
}


ちょっと不思議な記述に見えますが、列挙型の特徴として慣れておきましょう。

なお、列挙型は、まず一番上に列挙子を記述して、その次にフィールドやコンストラクタやメソッドを記述してください。

そうしないとコンパイルエラーになってしまいます。



列挙子は普通に文字列として出力すると、列挙子そのもの文字列を返します。

toStringメソッドをオーバーライドすることで、文字列をカスタマイズすることも可能です。

今回の問題では、その仕組みを使用しています。



また、列挙型はswitch文と非常に相性が良いです。

ただし、case文には「列挙型.列挙子」の形式ではなく、「列挙子」のみを記述しないとコンパイルエラーになります。



もちろん、次のようにif文を使って条件分岐することも可能です。

// 四季をループで回す
for(Season s : seasons) {

    if(s == Season.SUMMER) {
        System.out.println(s + "は暑い!!");
    } else if(s == Season.WINTER) {
        System.out.println(s + "は寒い!!");
    } else if(s == Season.SPRING || s == Season.FALL) {
        System.out.println(s);
    } else {
        // 現状はありえない
        throw new RuntimeException("Illegal Season!");
    }
}


参照型ではあまり比較演算子「==」を用いませんが、列挙型の場合は有効です。



列挙型の理解は深まったでしょうか。

コーディングしていて、この定数なんか使い勝手が悪いなと感じたら、是非列挙型の導入を検討してみてください。


参考図書



LINE公式アカウント

仕事が辛くてたまらない人生が、仕事が楽しくてたまらない人生に変わります。
【登録いただいた人全員に、無料キャリア相談プレゼント中!】


LineOfficial

友だち追加