やあ!
Enum
今日は、Java の特別なデータ型の 1 つ(「列挙」の略)について説明します。何が特別なのでしょうか?プログラムに「月」を実装するには何が必要かを想像してみましょう。 問題ないようですね?必要なのは、月にどのような特性があるかを判断することだけです。おそらく、最初に月の名前とその日数が必要になるでしょう。解決策は非常に簡単に見えます。
public class Month {
private String name;
private int daysCount;
public Month(String name, int daysCount) {
this.name = name;
this.daysCount = daysCount;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getDaysCount() {
return daysCount;
}
public void setDaysCount(int daysCount) {
this.daysCount = daysCount;
}
@Override
public String toString() {
return "Month{" +
"name='" + name + '\'' +
", daysCount=" + daysCount +
'}';
}
}
全部シャバン!Month
クラス、必須フィールド、ゲッター/セッター、および がありますtoString()
。もちろん、追加する必要がない限りequals()
、hashCode()
完全な幸福を達成するために:) しかし、ここで概念的な問題があります。おそらく覚えていると思いますが、OOP の主な利点の 1 つは、現実世界のエンティティを簡単にモデル化できることです。椅子、車、惑星など、日常生活のこれらすべての概念は、抽象化を利用してプログラムで簡単に表現できます。問題は、一部の現実世界のエンティティの値の範囲が厳密に制限されていることです。一年には季節が4つしかありません。1オクターブには8つの音しかありません。カレンダーにはたったの12か月しかありません。そして、Ocean's 11 の Danny Ocean の友達は 11 人だけです (ただし、これは問題ではありません:)) 重要なのは、通常の Java クラスではこれらのエンティティをモデル化し、その自然な制限を強制することができないということです。私たちのMonth
クラスには必須フィールドがすべて含まれています。しかし、別のプログラマがそれを使用すると、そのプログラマが完全に非常識なオブジェクトを作成するのを誰も止めることができません。
public class Main {
Month month1 = new Month("lolkek", 322);
Month month2 = new Month("yahoooooooooooo", 12345);
}
これがコードに現れた場合、犯人を見つけるのは簡単ではありません。一方では、オブジェクトを作成するプログラマは、Month
クラスが「年の月」を意味することに気づき、そのようなナンセンスを書かないかもしれません。一方、プログラマは、クラス設計者が提供した能力を利用するだけです。任意の名前や日数を付けることは可能ですか?まさにそれが私たちが得たものです。では、この状況では何をすべきでしょうか?正直なところ、Java 1.5 がリリースされる前は、プログラマは創造性を発揮する必要がありました :) 当時、彼らは次のような構造を作成していました。
public class Month {
private String name;
private int daysCount;
private Month(String name, int daysCount) {
this.name = name;
this.daysCount = daysCount;
}
public static Month JANUARY = new Month("January", 31);
public static Month FEBRUARY = new Month("February", 28);
public static Month MARCH = new Month("March", 31);
@Override
public String toString() {
return "Month{" +
"name='" + name + '\'' +
", daysCount=" + daysCount +
'}';
}
}
ここでは例を短くするために、月数を 12 か月から 3 か月に減らしています。このような設計により、問題を解決することが可能になりました。オブジェクトを作成する機能は、プライベート コンストラクターに制限されていました。
private Month(String name, int daysCount) {
this.name = name;
this.daysCount = daysCount;
}
このクラスを使用するプログラマは、単純にMonth
オブジェクトを作成することはできません。クラス開発者によって提供された最終的な静的オブジェクトを使用する必要がありました。たとえば、次のようになります。
public class Main {
public static void main(String[] args) {
Month january = Month.JANUARY;
System.out.println(january);
}
}
しかし、Java 開発者は既存の問題に注意を向けました。もちろん、プログラマーがその言語で利用可能なツールを使用して解決策を考え出すことができたのは素晴らしいことですが、それはそれほど簡単ではないようです。初心者にとっても、明確な解決策が必要でした。そしてEnum
Javaにも登場しました。基本的には、Enum
限られたオブジェクト値のセットを提供する Java クラスです。その様子は次のとおりです。
public enum Month {
JANUARY,
FEBRUARY,
MARCH
}
定義では、 はEnum
Java クラスであると示しましたが、本当にそうでしょうか? はい、それを検証することもできます。たとえば、Month
列挙型に他のクラスを継承させてみます。
public abstract class AbstractMonth {
}
// Error! The extends clause cannot be used with an enum
public enum Month extends AbstractMonth {
JANUARY,
FEBRUARY,
MARCH
}
なぜそのようなことが起こるのでしょうか? 書くとき:
public enum Month
コンパイラはこのステートメントを次のコードに変換します。
public Class Month extends Enum
すでにご存知のとおり、Java は多重継承をサポートしていません。したがって、 を継承することはできませんAbstractMonth
。この新しい構成要素 はどのようにEnum
使用できるのでしょうか? また、フィールドを含む古い構造とどう違うのでしょうかstatic final
? たとえば、古い構造ではswitch
ステートメント内で独自の値のセットを使用できませんでした。毎月祝われる祝日を思い出させるプログラムを作成したいと想像してください。
public class HolidayReminder {
public void printHolidays(Month month) {
switch (month) {
// Error!
case JANUARY:
}
}
}
ご覧のとおり、コンパイラはここでエラーをスローします。しかし、enum
Java 1.5 で登場すると、すべてがはるかに単純になりました。
public enum Month {
JANUARY,
FEBRUARY,
MARCH
}
public class HolidayReminder {
public void printHolidays(Month month) {
switch (month) {
case JANUARY:
System.out.println("New Year's Day is January 1st!");
break;
case FEBRUARY:
System.out.println("Valentine's Day is February 14th!");
break;
case MARCH:
System.out.println("Saint Patrick's Day is March 17th!");
break;
}
}
}
public class Main {
public static void main(String[] args) {
HolidayReminder reminder = new HolidayReminder();
reminder.printHolidays(Month.JANUARY);
}
}
コンソール出力:
New Year's Day is January 1st!
Enum
Java 1.5 以前と同様に、オブジェクト へのアクセスは静的なままであることに注意してください。Month
月にアクセスするためにオブジェクトを作成する必要はありません。Enum
列挙型を扱うときは、それが本格的なクラスであることを忘れないことが非常に重要です。これは、必要に応じてコンストラクターとメソッドを定義できることを意味します。たとえば、前のコード部分では、単に JANUARY、FEBRUARY、MARCH という値を指定しました。ただし、次のように列挙型を拡張できますMonth
。
public enum Month {
JANUARY("January", 31),
FEBRUARY("February", 28),
MARCH("March", 31),
APRIL("April", 30),
MAY("May", 31),
JUNE("June", 30),
JULY("July", 31),
AUGUST("August", 31),
SEPTEMBER("September", 30),
OCTOBER("October", 31),
NOVEMBER("November", 30),
DECEMBER("December", 31);
private String name;
private int daysCount;
Month(String name, int daysCount) {
this.name = name;
this.daysCount = daysCount;
}
public static Month[] getWinterMonths() {
return new Month[]{DECEMBER, JANUARY, FEBRUARY};
}
public static Month[] getSummerMonths() {
return new Month[]{JUNE, JULY, AUGUST};
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getDaysCount() {
return daysCount;
}
public void setDaysCount(int daysCount) {
this.daysCount = daysCount;
}
@Override
public String toString() {
return "Month{" +
"name='" + name + '\'' +
", daysCount=" + daysCount +
'}';
}
}
ここでは、enum
2 つのフィールド (月の名前と日数)、これらのフィールドを使用するコンストラクター、ゲッター/セッター、メソッドtoString()
、および 2 つの静的メソッドを指定しました。ご覧のとおり、これには問題はありませんでした。繰り返しますが、Enum
実際には本格的なクラスです。
import java.util.Arrays;
public class Main {
public static void main(String[] args) {
System.out.println(Arrays.toString(Month.getSummerMonths()));
}
}
コンソール出力:
[Month{name='June', daysCount=30}, Month{name='July', daysCount=31}, Month{name='August', daysCount=31}]
最後に、非常に役立つ Java の本、 Joshua Bloch 著の「Effective Java」をお勧めします。 著者は Java の作成者の 1 人なので、言語ツールを正しく適切に使用する方法についての彼のアドバイスは間違いなく信頼できます :) 私たちのレッスンに関しては、この本の の章に特に注意を払うことをお勧めしますEnum
。幸せな読書!:)
GO TO FULL VERSION