列挙型には、列挙型で定義された順序で配列を生成するために使用される value メソッドがあり、これを使用して走査できます。カスタム列挙クラスはすべて java.lang.Enum から継承しており、例では次の関数があります。
次のようにコードをコピーします。
//: 列挙型/EnumClass.java
// Enum クラスの機能
静的 net.mindview.util.Print.* をインポートします。
enum Shrubbery { 地面、這う、ぶら下がっている }
パブリック クラス EnumClass {
public static void main(String[] args) {
for(Shrubbery s : Shrubbery.values()) {
print(s + " ordinal: " + s.ordinal());
printnb(s.compareTo(Shrubbery.CRAWLING) + " ");
printnb(s.equals(Shrubbery.CRAWLING) + " ");
print(s == Shrubbery.CRAWLING);
print(s.getDeclaringClass());
print(s.name());
print("---------------------");
}
// 文字列名から列挙値を生成します。
for(String s : "吊り這う地面".split(" ")) {
低木低木 = Enum.valueOf(Shrubbery.class, s);
プリント(低木);
}
}
} /* 出力:
GROUND 序数: 0
-1 偽 偽
クラス低木
Joshua Bloch は、この章の作成に非常に役立ちました。
地面
-----------------------
クロール序数: 1
本当の本当の
クラス低木
這う
-----------------------
ハンギング序数: 2
偽 偽
クラス低木
ぶら下がっている
-----------------------
ぶら下がっている
這う
地面
*///:~
静的な列挙参照を使用することもできます。
次のようにコードをコピーします。
//: 列挙型/Spiciness.java
パッケージが列挙されます。
public enum 辛さ {NOT、MILD、MEDIUM、HOT、FLAMING} ///:~
//: 列挙型/Burrito.java
パッケージが列挙されます。
import static enumerated.Spiciness.*;
パブリッククラスブリトー{
辛さ度;
public Burrito(辛さ度) { this.degree = 度;}
public String toString() { return "ブリトーは "+ 度;}
public static void main(String[] args) {
System.out.println(新しいブリトー(NOT));
System.out.println(新しいブリトー(MEDIUM));
System.out.println(新しいブリトー(HOT));
}
} /* 出力:
ブリトーは違います
ブリトーはミディアムです
ブリトーは熱いです
*///:~
継承できないメソッドを列挙型に追加するだけでなく、列挙型を一般クラスとして扱うことができます。つまり、列挙型にメソッドを追加したり、列挙型に main メソッドを定義したりすることもできます。
次のようにコードをコピーします。
//: 列挙型/OzWitch.java
// オズ国の魔女たち。
静的 net.mindview.util.Print.* をインポートします。
パブリック列挙型 OzWitch {
// メソッドの前に、インスタンスを最初に定義する必要があります。
WEST(「ミス・ガルチ、別名西の悪い魔女」)、NORTH(「グリンダ、北の善い魔女」)、EAST(「東の悪い魔女、ルビーを身に着けている人」 + 「スリッパ、踏みつぶされる」ドロシーの家"),SOUTH("推測では問題ありませんが、行方不明です");
プライベート文字列の説明。
// コンストラクターはパッケージまたはプライベート アクセスである必要があります。
private OzWitch(文字列の説明) {
this.description = 説明;
}
public String getDescription() { 説明を返す }
public static void main(String[] args) {
for(OzWitch 魔女 : OzWitch.values())
print(witch + ": " +witch.getDescription());
}
} /* 出力:
西: ミス・ガルチ、別名西の悪い魔女
北: グリンダ、北の良い魔女
東: 東の悪い魔女、ルビーのスリッパを履いて、ドロシーの家に押しつぶされる
南: 推測では問題ありませんが、欠けています
*///:~
次のようにコードをコピーします。
//: 列挙型/SpaceShip.java
パブリック列挙型 SpaceShip {
スカウト、貨物、輸送、巡洋艦、戦艦、母艦。
public String toString() {
文字列 ID = 名前();
文字列下 = id.substring(1).toLowerCase();
id.charAt(0) + lower; を返します。
}
public static void main(String[] args) {
for(SpaceShip :values()) {
System.out.println;
}
}
} /* 出力:
スカウト
貨物
輸送
クルーザー
戦艦
母船
*///:~
switch ステートメントの列挙型 列挙型の重要な役割は switch ステートメントにあります。通常、switch ステートメントは整数値に対してのみ機能しますが、列挙型には整数の順序が組み込まれているため、インスタンスの順序は何らかの方法で決定できます。そのため、列挙型は switch ステートメントで使用できます。
次のようにコードをコピーします。
//: 列挙型/TrafficLight.java
// switch ステートメントの列挙型。
静的 net.mindview.util.Print.* をインポートします。
// 列挙型を定義します。
enum 信号 { 緑、黄、赤、 }
パブリック クラス TrafficLight {
信号の色 = Signal.RED;
public void change() {
スイッチ(色) {
// Signal.RED と言う必要はないことに注意してください。
// case ステートメント内:
赤の場合: カラー = Signal.GREEN;
壊す;
緑の場合: カラー = Signal.YELLOW;
壊す;
黄色の場合: カラー = Signal.RED;
壊す;
}
}
public String toString() {
return "信号機は " + color;
}
public static void main(String[] args) {
TrafficLight t = new TrafficLight();
for(int i = 0; i < 7; i++) {
印刷(t);
t.change();
}
}
} /* 出力:
信号機は赤です
信号は緑です
信号は黄色です
信号機は赤です
信号は緑です
信号は黄色です
信号機は赤です
*///:~
value()の秘密 以前はvaluesメソッドを使用していましたが、Enumを見るとvaluesメソッドが見つかりません。では、他に隠しメソッドがあるのでしょうか。単純なリフレクション コードを使用してそれをチェックできます。
次のようにコードをコピーします。
//: 列挙型/Reflection.java
// リフレクションを使用して列挙型を分析します。
インポート java.lang.reflect.*;
java.util.* をインポートします。
net.mindview.util.* をインポートします。
静的 net.mindview.util.Print.* をインポートします。
enum 探索 { ここ、そこ }
パブリック クラス リフレクション {
public static Set<String>analyze(Class<?> enumClass) {
print("----- 分析中 " + enumClass + " -----");
print("インターフェイス:");
for(タイプ t : enumClass.getGenericInterfaces())
印刷(t);
print("Base: " + enumClass.getSuperclass());
print("メソッド: ");
Set<String> メソッド = new TreeSet<String>();
for(メソッド m : enumClass.getMethods())
メソッド.add(m.getName());
print(メソッド);
メソッドを返す。
}
public static void main(String[] args) {
Set<String>exploreMethods =analyze(Explore.class);
Set<String> enumMethods =analyze(Enum.class);
print("Explore.containsAll(Enum)? " +
exploreMethods.containsAll(enumMethods));
printnb("Explore.removeAll(Enum): ");
exploreMethods.removeAll(enumMethods);
print(exploreMethods);
// enum のコードを逆コンパイルします。
OSExecute.command("javap Explore");
}
} /* 出力:
----- クラス Explore を分析中 -----
インターフェース:
ベース: クラス java.lang.Enum
方法:
[compareTo、equals、getClass、getDeclaringClass、hashCode、name、notify、notifyAll、ordinal、toString、valueOf、values、wait]
----- クラス java.lang.Enum を解析しています -----
インターフェース:
java.lang.Comparable<E>
インターフェースjava.io.Serializable
ベース: クラス java.lang.Object
方法:
[compareTo、equals、getClass、getDeclaringClass、hashCode、name、notify、notifyAll、ordinal、toString、valueOf、wait]
Explore.containsAll(Enum)?
Explore.removeAll(Enum): [値]
「Reflection.java」からコンパイル
最終クラス Explore extends java.lang.Enum{
public static Final ここで調べてください。
public static Final そこを探索してください。
public staticfinalExplore[]values();
public static Explore valueOf(java.lang.String);
静的 {};
}
*///:~
コンパイラによってvaluesメソッドが追加されていることがわかります。 valueOf メソッドも列挙の作成時にコンパイラによって追加されますが、Enum クラスにも valueOf メソッドがありますが、このメソッドには 2 つのパラメータがあるのに対し、コンパイラによって追加される valueOf メソッドにはパラメータが 1 つしかありません。列挙型はコンパイラによって最終的なものとして解釈されるため、列挙型を継承することはできません。バリューズメソッドはコンパイラによって追加された静的メソッドなので、列挙型をEnumにキャストするとバリューズメソッドは利用できなくなりますが、ClassにgetEnumConstantsメソッドがあるので、バリューズメソッドはありますが、 Enum では使用できませんが、 Class オブジェクトを通じて列挙インスタンスを取得できます。
次のようにコードをコピーします。
//: 列挙型/UpcastEnum.java
// enum をアップキャストする場合は、values() メソッドはありません
enum 検索 { 向こう、ヨン }
パブリック クラス UpcastEnum {
public static void main(String[] args) {
Search[] vals = Search.values();
Enum e = Search.HITHER; // アップキャスト
// e.values(); // Enum に value() がありません
for(Enum en : e.getClass().getEnumConstants())
System.out.println(ja);
}
} /* 出力:
ここまで
よん
*///:~
継承を使用しない実装 定義する列挙型はすべて java.lang.Enum から継承し、Java は多重継承をサポートしていないため、列挙型は継承を通じて作成されませんが、1 つ以上のインターフェイスを継承することによって列挙型を作成できます。
次のようにコードをコピーします。
//: 列挙/漫画/EnumImplementation.java
// enum はインターフェイスを実装できます
パッケージは列挙されています。漫画。
java.util.* をインポートします。
net.mindview.util.* をインポートします。
enum CartoonCharacter は Generator<CartoonCharacter> { を実装します。
ベタベタ、スパンキー、パンチの効いた、愚かな、弾むような、ナッティな、ボブ。
プライベートランダム rand = new Random(47);
public CartoonCharacter next() {
戻り値()[rand.nextInt(values().length)];
}
}
パブリック クラス EnumImplementation {
public static <T> void printNext(Generator<T> rg) {
System.out.print(rg.next() + ", ");
}
public static void main(String[] args) {
// 任意のインスタンスを選択します:
CartoonCharacter cc = CartoonCharacter.BOB;
for(int i = 0; i < 10; i++)
プリントネクスト(cc);
}
} /* 出力:
ボブ、パンチー、ボブ、スパンキー、ナッティ、パンチー、スラッピー、ナッティ、ナッティ、スラッピー、
*///:~
ランダムな選択 後の例の多くでは、列挙インスタンスからオブジェクトをランダムに選択します。これを実装するパブリック クラスを作成します。
次のようにコードをコピーします。
//: net/mindview/util/Enums.java
パッケージ net.mindview.util;
java.util.* をインポートします。
パブリック クラス列挙型 {
プライベート静的ランダム rand = new Random(47);
public static <T extends Enum<T>> Trandom(Class<T> ec) {
ランダムを返します(ec.getEnumConstants());
}
public static <T> T ランダム(T[] 値) {
戻り値[rand.nextInt(values.length)];
}
} ///:~
次のようにコードをコピーします。
//: 列挙型/RandomTest.java
net.mindview.util.* をインポートします。
enum アクティビティ { 座る、寝る、立つ、ホッピング、走る、避ける、ジャンプする、落ちる、飛ぶ }
パブリック クラス RandomTest {
public static void main(String[] args) {
for(int i = 0; i < 20; i++)
System.out.print(Enums.random(Activity.class) + " ");
}
} /* 出力:
立っている 飛んでいる 走っている 立っている 走っている 立っている 横たわっている かわす 座っている ランニング ホッピング ホッピング ホッピング ランニング 立っている 横たわっている 落ちる ランニング 飛んでいる 横たわっている
*///:~
インターフェイスを使用して列挙を整理することは継承できません。そのため、継承によって列挙の数を拡張したい場合や、列挙をグループ化する必要がある場合があるため、不便が生じることがあります。後者の場合、インターフェイス内でグループ化された列挙を定義し、このインターフェイスから継承して列挙を作成できます。次のように、列挙として作成する必要があるさまざまな食品カテゴリがありますが、各カテゴリをタイプとして定義する必要があります。食品の内容は以下の通りです。
次のようにコードをコピーします。
//: 列挙/menu/Food.java
// インターフェイス内の列挙型のサブカテゴリ化。
パッケージの列挙型.メニュー;
パブリック インターフェイス Food {enum Appetizer は Food {SALAD、SOUP、SPRING_ROLLS;} を実装します。
enum MainCourse は Food {LASAGNE、BURRITO、PAD_THAI、LENTILS、HUMMOUS、VINDALOO;} を実装します。
enum デザートはフード {TIRAMISU、GELATO、BLACK_FOREST_CAKE、FRUIT、CREME_CARAMEL;} を実装します。
enum コーヒーは食品を実装します {BLACK_COFFEE, DECAF_COFFEE, ESPRESSO,LATTE, CAPPUCCINO, TEA, HERB_TEA;}
} ///:~
各列挙はインターフェイスの実装として定義されているため、次のように各列挙の型は Food になります。
次のようにコードをコピーします。
//: 列挙/menu/TypeOfFood.java
パッケージの列挙型.メニュー;
static enumerated.menu.Food.* をインポートします。
パブリック クラス TypeOfFood {
public static void main(String[] args) {
食べ物 = 前菜.サラダ;
食べ物 = MainCourse.LASAGNE;
food = デザート.GELATO;
食べ物 = コーヒー.カプチーノ;
}
} ///:~
ただし、インターフェイスは列挙型のような複数の型を操作できないため、列挙型の列挙が必要な場合は、各列挙型のインスタンスを列挙型にカプセル化できます。
次のようにコードをコピーします。
//: 列挙型/menu/Course.java
パッケージの列挙型.メニュー;
net.mindview.util.* をインポートします。
public enum コース {
APPETIZER(Food.Appetizer.class)、MAINCOURSE(Food.MainCourse.class)、DESSERT(Food.Dessert.class)、COFFEE(Food.Coffee.class);
プライベート Food[] 値。
プライベートコース(クラス<?拡張食>種類) {
値 = kind.getEnumConstants();
}
public Food randomSelection() {
Enums.random(値)を返します;
}
} ///:~
各列挙では、対応するコンストラクター パラメーターとして Class オブジェクトを使用し、このパラメーターから getEnumConstants を使用して列挙インスタンスを取得し、randomSelection メソッドで使用してランダムな食事を生成できます。
次のようにコードをコピーします。
//: 列挙/menu/Meal.java
パッケージの列挙型.メニュー;
パブリッククラスの食事 {
public static void main(String[] args) {
for(int i = 0; i < 5; i++) {
for(コースコース:Course.values()) {
食べ物 food = course.randomSelection();
System.out.println(食べ物);
}
System.out.println("---");
}
}
} /* 出力:
春巻
ヴィンダルー
フルーツ
DECAF_COFFEE
---
スープ
ヴィンダルー
フルーツ
お茶
---
サラダ
ブリトー
フルーツ
お茶
---
サラダ
ブリトー
クリームキャラメル
ラテ
---
スープ
ブリトー
ティラミス
エスプレッソ
---
*///:~
よりコンパクトな実装は次のとおりです。
次のようにコードをコピーします。
//: 列挙型/SecurityCategory.java
// 列挙型のより簡潔なサブカテゴリ化。
net.mindview.util.* をインポートします。
enum SecurityCategory {
STOCK(Security.Stock.class)、BOND(Security.Bond.class);
Security[] 値。
SecurityCategory(Class<? extends Security> kind) {
値 = kind.getEnumConstants();
}
インターフェイスのセキュリティ {
enum Stock はセキュリティ { SHORT、LONG、MARGIN } を実装します
enum Bond はセキュリティ { MUNICIPAL, JUNK } を実装します
}
パブリックセキュリティのrandomSelection() {
Enums.random(値)を返します;
}
public static void main(String[] args) {
for(int i = 0; i < 10; i++) {
SecurityCategory カテゴリ = Enums.random(SecurityCategory.class);
System.out.println(カテゴリ + ": " +
category.randomSelection());
}
}
} /* 出力:
債券: 市営債券
債券: 市営債券
株式: マージン
株式: マージン
ボンド:ジャンク
在庫: ショート
在庫: ロング
在庫: ロング
債券: 市営債券
ボンド:ジャンク
*///:~
次のようにコードをコピーします。
//: 列挙/menu/Meal2.java
パッケージの列挙型.メニュー;
net.mindview.util.* をインポートします。
public enum Meal2 {
APPETIZER(Food.Appetizer.class)、MAINCOURSE(Food.MainCourse.class)、DESSERT(Food.Dessert.class)、COFFEE(Food.Coffee.class);
プライベート Food[] 値。
private Meal2(Class<? extends Food> kind) {
値 = kind.getEnumConstants();
}
パブリック インターフェイス 食品 {
enum Appetizer は Food {SALAD、SOUP、SPRING_ROLLS;} を実装します。
enum MainCourse は Food {LASAGNE、BURRITO、PAD_THAI、LENTILS、HUMMOUS、VINDALOO;} を実装します。
enum デザートはフード {TIRAMISU、GELATO、BLACK_FOREST_CAKE、FRUIT、CREME_CARAMEL;} を実装します。
enum コーヒーは食品を実装します {BLACK_COFFEE, DECAF_COFFEE, ESPRESSO,LATTE, CAPPUCCINO, TEA, HERB_TEA;}
}
public Food randomSelection() {
Enums.random(値)を返します;
}
public static void main(String[] args) {
for(int i = 0; i < 5; i++) {
for(Meal2 食事 : Meal2.values()) {
食べ物 food = Meal.randomSelection();
System.out.println(食べ物);
}
System.out.println("---");
}
}
} /* Meal.java と同じ出力 *///:~
フラグの代わりに EnumSet を使用する EnumSet は Java SE5 で追加され、列挙型と Set を組み合わせて整数ベースのビット フラグを置き換えます。ビット フラグは通常、ある種の情報の切り替えを示すために使用されますが、コードではビットは意味のある概念ではなく操作されるため、理解するのは簡単ではありません。 EnumSet はビット フラグよりも高速で、内部的には long を使用してビット ベクトルを表現するため、効率を気にせずに、より概念的な言語を使用して特定のビットの切り替えを表現できます。 EnumSet の要素は同じ列挙から取得する必要があります。次はアラーム位置の列挙を定義します。
次のようにコードをコピーします。
//: 列挙型/AlarmPoints.java
パッケージが列挙されます。
public enum AlarmPoints {STAIR1, STAIR2, LOBBY, OFFICE1, OFFICE2, OFFICE3,OFFICE4, BATHROOM, UTILITY, KITCHEN} ///:~
次に、EnumSet クラスを使用してアラームのステータスを追跡します。
次のようにコードをコピーします。
//: 列挙型/EnumSets.java
// EnumSet に対する操作
パッケージが列挙されます。
java.util.* をインポートします。
静的に列挙された.AlarmPoints.*; をインポートします。
静的 net.mindview.util.Print.* をインポートします。
パブリック クラス EnumSets {
public static void main(String[] args) {
EnumSet<AlarmPoints> ポイント = EnumSet.noneOf(AlarmPoints.class); // 空のセット
ポイント.追加(バスルーム);
print(ポイント);
Points.addAll(EnumSet.of(STAIR1, STAIR2, KITCHEN));
print(ポイント);
ポイント = EnumSet.allOf(AlarmPoints.class);
Points.removeAll(EnumSet.of(STAIR1, STAIR2, KITCHEN));
print(ポイント);
Points.removeAll(EnumSet.range(OFFICE1, OFFICE4));
print(ポイント);
ポイント = EnumSet.complementOf(ポイント);
print(ポイント);
}
} /* 出力:
[バスルーム]
[STAIR1、STAIR2、バスルーム、キッチン]
[ロビー、オフィス1、オフィス2、オフィス3、オフィス4、バスルーム、ユーティリティ]
[ロビー、バスルーム、ユーティリティ]
[STAIR1、STAIR2、オフィス1、オフィス2、オフィス3、オフィス4、キッチン]
*///:~
EnumSet は long 型に基づいて構築されており、64 ビットを持っています。そのため、列挙型がこの数を超えた場合はどうなるでしょうか?
次のようにコードをコピーします。
//: 列挙型/BigEnumSet.java
java.util.* をインポートします。
パブリック クラス BigEnumSet {
enum Big { A0、A1、A2、A3、A4、A5、A6、A7、A8、A9、A10、A11、A12、A13、A14、A15、A16、A17、A18、A19、A20、A21、A22、A23 、A24、A25、A26、A27、A28、A29、 A30、A31、A32、
A33、A34、A35、A36、A37、A38、A39、A40、A41、A42、A43、A44、A45、A46、A47、A48、A49、A50、A51、A52、A53、A54、A55、A56、A57、 A58、A59、A60、A61、 A62、A63、A64、A65、
A66、A67、A68、A69、A70、A71、A72、A73、A74、A75}
public static void main(String[] args) {
EnumSet<Big> bigEnumSet = EnumSet.allOf(Big.class);
System.out.println(bigEnumSet);
}
} /* 出力:
[A0、A1、A2、A3、A4、A5、A6、A7、A8、A9、A10、A11、A12、A13、A14、A15、A16、A17、A18、A19、A20、A21、A22、A23、A24 、A25、A26、A27、A28、A29、A30、 A31、A32、A33、A34、A35、A36、A37、A38、A39、A40、A41、A42、A43、A44、A45、A46、A47、A48、A49、A50、A51、A52、A53、A54、A55、 A56、A57、A58、A59、 [A60、A61、A62、A63、A64、A65、A66、A67、A68、A69、A70、A71、A72、A73、A74、A75]
*///:~
プログラムが正常に実行されていることがわかります。そのため、EnumMap を使用した列挙型に対応するために、long 型が内部的に追加されている可能性が非常に高いです。
EnumMap は特別なタイプの Map であり、そのキーの値は同じ列挙内のタイプのみであるため、EnumMap は配列を通じて内部的に実装でき、非常に効率的です。
次のようにコードをコピーします。
//: 列挙型/EnumMaps.java
// EnumMap の基本。
パッケージが列挙されます。
java.util.* をインポートします。
静的に列挙された.AlarmPoints.*; をインポートします。
静的 net.mindview.util.Print.* をインポートします。
インターフェースコマンド { void action() }
パブリック クラス EnumMaps {
public static void main(String[] args) {
EnumMap<AlarmPoints,Command> em = new EnumMap<AlarmPoints,Command>(AlarmPoints.class);
em.put(キッチン, 新しいコマンド() {
public void action() { print("キッチン火災!");
});
em.put(バスルーム, 新しいコマンド() {
public void action() { print("トイレ警告!");
});
for(Map.Entry<AlarmPoints,Command> e : em.entrySet()) {
printnb(e.getKey() + ": ");
e.getValue().action();
}
try { // 特定のキーに値がない場合:
em.get(UTILITY).action();
} catch(例外 e) {
印刷(e);
}
}
} /* 出力:
バスルーム: バスルーム警告!
キッチン: キッチンで火災が発生しました!
java.lang.NullPointerException
*///:~
特定の定数メソッド Java 列挙型には非常に興味深い機能があります。つまり、列挙型インスタンスごとに異なる動作を定義できるということです。これを実現するには、列挙型の一部として 1 つ以上の抽象メソッドを定義し、それぞれに異なる動作を定義します。列挙インスタンス定義メソッド:
次のようにコードをコピーします。
//: 列挙型/ConstantSpecificMethod.java
java.util.* をインポートします。
java.text.* をインポートします。
public enum ConstantSpecificMethod {
DATE_TIME {String getInfo() {return DateFormat.getDateInstance().format(new Date());}},
CLASSPATH {String getInfo() {return System.getenv("CLASSPATH");}},
VERSION {String getInfo() {return System.getProperty("java.version");}};
抽象文字列 getInfo();
public static void main(String[] args) {
for(ConstantSpecificMethod csm :values())
System.out.println(csm.getInfo());
}
} /* (実行して出力を確認します) *///:~
上記のコードは、各列挙要素が異なる要素であり、すべての要素が ConstantSpecificMethod 基本クラスから継承しているという点で行き詰まっているように見えますが、列挙要素を型として扱うことができないため、この方法では実際には理解できません。
次のようにコードをコピーします。
//: 列挙型/NotClasses.java
// {実行: javap -c LikeClasses}
静的 net.mindview.util.Print.* をインポートします。
enum LikeClasses {WINKEN { void behavior() { print("Behavior1") } },BLINKEN { void behavior() { print("Behavior2") } },NOD { void behavior() { print("Behavior3"); ; } };
抽象的な void 動作();
}
パブリック クラス NotClasses {
// void f1(LikeClasses.WINKEN インスタンス) {} // いいえ
} /* 出力:
「NotClasses.java」からコンパイル
抽象クラス LikeClasses extends java.lang.Enum{
public static Final LikeClasses WINKEN;
public static Final LikeClasses 点滅します。
public static Final LikeClasses NOD;
...
*///:~
別の例として、洗車メニューがあるとします。次のように、顧客はさまざまなメニューに基づいてさまざまなサービスを選択し、EnumSet を使用してメニューをサービスに関連付けることができます。
次のようにコードをコピーします。
//: 列挙型/CarWash.java
java.util.* をインポートします。
静的 net.mindview.util.Print.* をインポートします。
パブリック クラス CarWash {
public enum Cycle {UNDERBODY {void action() { print("アンダーボディにスプレーする");
ホイールウォッシュ {void action() { print("ホイールの洗浄");
PRWASH {void action() { print("汚れを落とす");
BASIC {void action() { print("基本的なウォッシュ")};
HOTWAX {void action() { print("ホットワックスの塗布");
RINSE {void action() { print("すすぎ") }};
BLOWDRY {void action() { print("ブロー乾燥");
抽象的な void アクション();
}
EnumSet<Cycle> サイクル = EnumSet.of(Cycle.BASIC, Cycle.RINSE);
public void add(サイクルサイクル) { サイクル.add(サイクル) }
public void washCar() {
for(サイクル c : サイクル)
c.action();
}
public String toString() { returncycles.toString(); }
public static void main(String[] args) {
CarWash 洗浄 = new CarWash();
プリント(ウォッシュ);
wash.washCar();
// 追加の順序は重要ではありません。
wash.add(Cycle.BLOWDRY);
wash.add(Cycle.BLOWDRY); // 重複は無視されます。
wash.add(サイクル.RINSE);
wash.add(Cycle.HOTWAX);
プリント(ウォッシュ);
wash.washCar();
}
} /* 出力:
【ベーシック、リンス】
基本的な洗い方
すすぎ
[ベーシック、ホットワックス、リンス、ブロー]
基本的な洗い方
ホットワックスを塗布する
すすぎ
送風乾燥
*///:~
次のように、継承された抽象メソッドを使用する代わりに、デフォルトの特定の定数メソッドをオーバーライドすることもできます。
次のようにコードをコピーします。
//: 列挙型/OverrideConstantSpecific.java
静的 net.mindview.util.Print.* をインポートします。
public enum OverrideConstantSpecific {
ナット、ボルト、ワッシャー {void f() { print("オーバーライドされたメソッド");
void f() { print("デフォルトの動作");
public static void main(String[] args) {
for(OverrideConstantSpecific ocs :values()) {
printnb(ocs + ": ");
ocs.f();
}
}
} /* 出力:
NUT: デフォルトの動作
BOLT: デフォルトの動作
WASHER: オーバーライドされたメソッド
*///:~
場合によっては、チェーン内の特定のリクエストを渡したいことがあります。これは、特定の定数メソッドを使用することで簡単に実現できます。
次のようにコードをコピーします。
//: 列挙型/PostOffice.java
// 郵便局をモデル化します。
列挙型 743
java.util.* をインポートします。
net.mindview.util.* をインポートします。
静的 net.mindview.util.Print.* をインポートします。
クラスメール{
// NO はランダム選択の確率を低くします。
enum Generaldelivery {YES,NO1,NO2,NO3,NO4,NO5}
enum スキャン可能性 {UNSCANNABLE,YES1,YES2,YES3,YES4}
enum の可読性 {ILLEGIBLE,YES1,YES2,YES3,YES4}
enum アドレス {INCORRECT,OK1,OK2,OK3,OK4,OK5,OK6}
enum ReturnAddress {MISSING,OK1,OK2,OK3,OK4,OK5}
一般配送一般配送;
スキャン可能性スキャン可能性。
可読性 可読性。
住所住所;
ReturnAddress returnAddress;
静的ロングカウンター = 0;
長い ID = counter++;
public String toString() { return "メール" + id;
public String 詳細() {
return toString() + "、一般配信: " + generaldelivery + "、住所のスキャン可能性: " + スキャン可能性 + "、住所の可読性: " + 可読性 +
"、アドレス アドレス: " + アドレス + "、戻りアドレス: " + returnAddress;
}
//テストメールを生成:
パブリック静的メールrandomMail() {
メール m = new Mail();
m.generaldelivery= Enums.random(Generaldelivery.class);
m.scannability = Enums.random(Scannability.class);
m.readability = Enums.random(Readability.class);
m.address = Enums.random(Address.class);
m.returnAddress = Enums.random(ReturnAddress.class);
mを返します。
}
public static Iterable<Mail> ジェネレーター(最終 int カウント) {
return new Iterable<Mail>() {
int n = カウント;
public Iterator<Mail> iterator() {
return new Iterator<Mail>() {
public boolean hasNext() { return n-- > 0;
public Mail next() { returnrandomMail(); }
public void Remove() { // 未実装
新しい UnsupportedOperationException() をスローします。
}
};
}
};
}
}
パブリック クラス PostOffice {
enum MailHandler {
GENERAL_DELIVERY {
ブールハンドル(メールm) {
switch(m.generaldelivery) {
はいの場合:
print("配達は局留めを使用します。" + m);
true を返します。
デフォルト: false を返します。
}
}
}、
マシンスキャン {
ブールハンドル(メールm) {
switch(m.scannability) {
スキャンできない場合: false を返します。
デフォルト:
スイッチ(m.アドレス) {
間違った場合: false を返します。
デフォルト:
print(""+ m + " を自動的に配信します");
true を返します。
}
}
}
}、
VISUAL_INSPECTION {
ブールハンドル(メールm) {
switch(m.readability) {
判読できない場合: false を返します。
デフォルト:
スイッチ(m.アドレス) {
間違った場合: false を返します。
デフォルト:
print("「 + m + " を正常に配信します」);
true を返します。
}
}
}
}、
RETURN_TO_SENDER {
ブールハンドル(メールm) {
switch(m.returnAddress) {
ケースが見つからない場合: false を返します。
デフォルト:
print("送信者に " + m + " を返します");
true を返します。
}
}
};
抽象ブールハンドル(Mail m);
}
静的 void ハンドル(メール m) {
for(MailHandler ハンドラー : MailHandler.values())
if(ハンドラー.ハンドル(m))
戻る;
print(m + " はデッドレターです");
}
public static void main(String[] args) {
for(メール メール : Mail.generator(10)) {
print(mail.details());
ハンドル(メール);
print("*****");
}
}
} /* 出力:
メール 0、局留め: NO2、住所スキャン可能性: UNSCANNABLE、住所読み取り可能性: YES3、宛先住所: OK1、返送先住所: OK1
メール0を正常に配信中
*****
メール 1、局留め: NO5、住所読み取り可能性: YES3、住所読み取り可能性: 判読不能、宛先住所: OK5、返送先住所: OK1
メール1を自動配信する
*****
メール 2、局留め:YES、住所読み取り可能性:YES3、住所読み取り可能性:YES1、宛先住所:OK1、返送先住所:OK5
郵便局留め2を利用する場合
*****
メール 3、局留め: NO4、住所スキャン可能性: YES3、住所読み取り可能性: YES1、宛先住所: INCORRECT、返送先住所: OK4
メール 3 を差出人に返送する
*****
メール 4、局留め: NO4、住所スキャン可能性: UNSCANNABLE、住所読み取り可能性: YES1、住所住所: INCORRECT、返送先住所: OK2
メール 4 を差出人に返送する
*****
メール 5、局留め: NO3、住所読み取り可能性: YES1、住所読み取り可能性: 判読不能、宛先住所: OK4、返送先住所: OK2
メール5を自動配信する
*****
メール 6、局留め: YES、住所読み取り可能性: YES4、住所読み取り可能性: 判読不能、住所宛先: OK4、返送先住所: OK4
メール6の局留め利用について
*****
メール 7、局留め: YES、住所スキャン可能性: YES3、住所読み取り可能性: YES4、住所宛先: OK2、返送先住所: MISSING
メール7の局留め利用について
*****
メール 8、局留め: NO3、住所スキャン可能性: YES1、住所読み取り可能性: YES3、住所住所: INCORRECT、返送先住所: MISSING
メール 8 はデッドレターです
*****
メール 9、局留め: NO1、住所スキャン可能性: UNSCANNABLE、住所読み取り可能性: YES2、宛先住所: OK1、返送先住所: OK4
メール9を正常に配信中
*****
*///:~
列挙型は、ステート マシンを作成するのに理想的なタイプでもあります。ステート マシンは、入力に応じて限られた数の状態間を移動し、特定の状態に達すると作業を終了します。対応する出力は、列挙内でさまざまな入力を定義する典型的なステート マシンの例です。
次のようにコードをコピーします。
//: 列挙型/Input.java
パッケージが列挙されます。
java.util.* をインポートします。
public enum 入力 {
ニッケル(5)、ダイム(10)、クォーター(25)、ドル(100)、歯磨き粉(200)、チップス(75)、ソーダ(100)、SOAP(50)、ABORT_TRANSACTION {
public int amount() { // 許可しない
throw new RuntimeException("ABORT.amount()");
}
}、
STOP { // これが最後のインスタンスでなければなりません。
public int amount() { // 許可しない
throw new RuntimeException("SHUT_DOWN.amount()");
}
};
int 値; // セント単位
入力(int値) { this.value = 値 }
入力() {}
int amount() { 戻り値 };
静的ランダム rand = new Random(47);
パブリック静的入力randomSelection() {
// STOP は含めないでください:
戻り値()[rand.nextInt(values().length - 1)];
}
} ///:~
VendingMachine は、入力に応答するために使用されます。まず、Category 列挙によって入力を分類し、次に switch ステートメントを使用します。
次のようにコードをコピーします。
//: 列挙型/VendingMachine.java
// {引数: VendingMachineInput.txt}
パッケージが列挙されます。
java.util.* をインポートします。
net.mindview.util.* をインポートします。
静的に列挙された.Input.*をインポートします。
静的 net.mindview.util.Print.* をインポートします。
列挙型カテゴリ {
MONEY(ニッケル、ダイム、クォーター、ドル)、ITEM_SELECTION(歯磨き粉、チップス、ソーダ、石鹸)、QUIT_TRANSACTION(ABORT_TRANSACTION)、SHUT_DOWN(STOP);
プライベートInput[]値;
カテゴリ(入力...タイプ) { 値 = タイプ }
private static EnumMap<Input,Category> カテゴリ = new EnumMap<Input,Category>(Input.class);
静的 {
for(カテゴリc:Category.class.getEnumConstants())
for(入力タイプ: c.values)
カテゴリ.put(タイプ, c);
}
public static カテゴリ category(入力 input) {
カテゴリを返します.get(入力);
}
}
パブリック クラス VendingMachine {
プライベート静的状態 state = State.RESTING;
プライベート静的整数 = 0;
プライベート静的入力選択 = null;
enum StateDuration {TRANSIENT} //タグ付け列挙型
enum 状態 {
休憩中 {
void next(入力入力) {
switch(カテゴリ.カテゴリ化(入力)) {
ケース金額:
金額 += input.amount();
状態 = ADDING_MONEY;
壊す;
ケースSHUT_DOWN:
状態 = ターミナル;
デフォルト:
}
}
}、
追加_お金 {
void next(入力入力) {
switch(カテゴリ.カテゴリ化(入力)) {
ケース金額:
金額 += input.amount();
壊す;
ケース ITEM_SELECTION:
選択 = 入力;
if(金額 < 選択.金額())
print("お金が足りません " + 選択);
それ以外の状態 = 調剤中;
壊す;
QUIT_TRANSACTION の場合:
状態 = GIVING_CHANGE;
壊す;
ケースSHUT_DOWN:
状態 = ターミナル;
デフォルト:
}
}
}、
DISPENSING(StateDuration.TRANSIENT) {
void next() {
print("ここにあなたの " + 選択内容があります);
金額 -= 選択.金額();
状態 = GIVING_CHANGE;
}
}、
GIVING_CHANGE(StateDuration.TRANSIENT) {
void next() {
if(金額 > 0) {
print("お釣り: " + 金額);
金額 = 0;
}
状態 = 休息中。
}
}、
ターミナル { void Output() { print("停止") } };
プライベートブール値 isTransient = false;
州() {}
State(StateDuration trans) { isTransient = true }
void next(入力入力) {
throw new RuntimeException("非一時状態の場合は " + "next(Input input) のみを呼び出します");
}
void next() {
throw new RuntimeException(" + "StateDuration.TRANSIENT 状態の場合のみ next() を呼び出します");
}
void 出力() { 出力(量) }
}
static void run(Generator<Input> gen) {
while(state != State.TERMINAL) {
state.next(gen.next());
while(state.isTransient)
state.next();
状態.出力();
}
}
public static void main(String[] args) {
Generator<Input> gen = new RandomInputGenerator();
if(引数.長さ == 1)
gen = 新しい FileInputGenerator(args[0]);
実行(生成);
}
}
// 基本的な健全性チェックの場合:
class RandomInputGenerator は Generator<Input> {を実装します。
public Input next() { return Input.randomSelection(); }
}
// 「;」で区切られた文字列のファイルから入力を作成します。
class FileInputGenerator は Generator<Input> {を実装します。
プライベート Iterator<String> 入力;
public FileInputGenerator(String fileName) {
input = new TextFile(ファイル名, ";").iterator();
}
public 入力 next() {
if(!input.hasNext())
null を返します。
return Enum.valueOf(Input.class, input.next().trim());
}
} /* 出力:
これがあなたのチップです
これがあなたの歯磨き粉です
お釣り: 35
SODAのお金が足りない
SODAのお金が足りない
お釣り: 75
停止中
*///:~
上記の出力を生成するために使用されたテスト データは次のとおりです。
次のようにコードをコピーします。
クォーター; クォーター;
ドル; 歯磨き粉;
四半期; ABORT_TRANSACTION;
クォーター; ソーダ;
10セント硬貨;
ABORT_TRANSACTION;
停止;
///:~
複数のアンパック 複数の型間の相互作用を扱う場合、Number.plush(Number)、Number.mutiply(Number) などのコードが乱雑になる可能性があります。 Number はファミリーの単なる基本クラスであるため、呼び出したときにa.plus(b) の場合、a の型も b の型もわかりません。では、それらの間の相互作用が正しいことを確認するにはどうすればよいでしょうか? Java は単一のアンパックしか実行できません。つまり、複数の未知の型の 1 つ以上の操作が実行される場合、Java はいずれかの型に対して動的バインディング メカニズムしか実行できません。これでは、上で説明した問題を解決できません。動的バインディング コードを手動で作成します。
解決策はマルチバインディングを使用することです。ポリモーフィズムはメソッドを呼び出す場合にのみ発生するため、複数の解凍が必要な場合は複数のメソッドを呼び出す必要があります。複数のアンラップを行う場合は、各型のメソッドを呼び出してアンラップする仮想メソッドが必要です。次の例はじゃんけんの例です。
次のようにコードをコピーします。
//: 列挙型/Outcome.java
パッケージが列挙されます。
public enum 結果 { 勝ち、負け、引き分け } ///:~
//: 列挙型/RoShamBo1.java
// 複数のディスパッチングのデモ。
パッケージが列挙されます。
java.util.* をインポートします。
静的に列挙された.Outcome.*をインポートします。
インターフェース項目 {
結果を競う(アイテムそれ);
結果の評価(論文p);
結果の評価(はさみ);
結果の eval(Rock r);
}
クラス Paper は、Item { を実装します。
public 結果compare(Item it) { return it.eval(this) }
public Outcome eval(Paper p) { return DRAW }
public Outcome eval(Scissors) { return WIN }
public Outcome eval(Rock r) { return LOSE }
public String toString() { return "紙";
}
クラス Scissors は、Item {を実装します。
public 結果compare(Item it) { return it.eval(this) }
public Outcome eval(Paper p) { return LOSE }
public Outcome eval(Scissors) { return DRAW }
public 結果 eval(Rock r) { return WIN }
public string toString(){"shissors";
}
クラスロックはアイテムを実装しています{
パブリック結果競合(it){return it.eval(this);
パブリック結果eval(Paper P){return win}
パブリック結果eval(はさみ){return lose;
パブリック結果eval(rock r){return draw;}
public string toString(){return "rock";
}
パブリッククラスroshambo1 {
静的最終intサイズ= 20;
private static Random Rand = new Random(47);
public staticアイテムnewItem(){
switch(rand.nextint(3)){
デフォルト:
ケース0:新しいハサミ()を返します。
ケース1:新しい論文()を返します。
ケース2:new Rock()を返します。
}
}
public static void一致(項目A、アイテムB){
System.out.println(a + "vs." + b + ":" + a.compete(b));
}
public static void main(String[] args) {
for(int i = 0; i <size; i ++)
match(newItem()、newItem());
}
} /*出力:
ロックvs.ロック:ドロー
紙とロック:勝利
紙とロック:勝利
紙とロック:勝利
はさみvs.紙:勝利
はさみ対はさみ:描画
はさみvs.紙:勝利
ロックvs.紙:負けます
紙と紙:描画
ロックvs.紙:負けます
紙とはさみ:負けます
紙とはさみ:負けます
ロックvs.はさみ:勝利
ロックvs.紙:負けます
紙とロック:勝利
はさみvs.紙:勝利
紙とはさみ:負けます
紙とはさみ:負けます
紙とはさみ:負けます
紙とはさみ:負けます
*///:〜
複数の開梱を実現するために多くの方法を使用しましたが、入手したのは良いコード構造でした。列挙ソリューションを使用して上記のコードを実装する場合の最大の問題は、列挙インスタンスがタイプではないため、列挙インスタンスをパラメータータイプとして使用できないことです。しかし、この障害を回避する他の方法があります。
次のようにコードをコピーします。
//:Enumerated/Roshambo2.java
// 1つの列挙を別の列挙に切り替えます。
パッケージが列挙されています。
Static Enumerated.outcome。*;
Public Enum Roshambo2は競争相手<roshambo2> {{
紙(描画、負け、勝利)、はさみ(勝ち、引き分け、失う)、ロック(負け、勝ち、描画);
Private out out vpaper、vscissors、vrock;
Roshambo2(結果論文、結果のハサミ、アウトカムロック){
this.vpaper = Paper;
this.vscissors = shissors;
this.vrock = rock;
}
パブリック結果は競合する(roshambo2 it){
switch(it){
デフォルト:
ケースペーパー:VPAPERを返します。
ケースハサミ:VSCISSORSを返します。
ケースロック:vrockを返します。
}
}
public static void main(String[] args) {
Roshambo.play(roshambo2.class、20);
}
} /*出力:
ロックvs.ロック:ドロー
はさみvs.ロック:負けます
はさみvs.ロック:負けます
列挙されたタイプ753
はさみvs.ロック:負けます
紙とはさみ:負けます
紙と紙:描画
紙とはさみ:負けます
ロックvs.はさみ:勝利
はさみ対はさみ:描画
ロックvs.はさみ:勝利
はさみvs.紙:勝利
はさみvs.紙:勝利
ロックvs.紙:負けます
ロックvs.はさみ:勝利
はさみvs.ロック:負けます
紙とはさみ:負けます
はさみvs.紙:勝利
はさみvs.紙:勝利
はさみvs.紙:勝利
はさみvs.紙:勝利
*///:〜
次のようにコードをコピーします。
//:ENUMERATED/COMPERITOR.JAVA
// 1つの列挙を別の列挙に切り替えます。
パッケージが列挙されています。
パブリックインターフェイス競合他社<t拡張競合他社<t >> {
アウトカム競合(T競合他社);
} ///:〜
次のようにコードをコピーします。
//:Enumerated/Roshambo.java
// Roshamboの例の一般的なツール。
パッケージが列挙されています。
net.mindview.util。*をインポートします。
パブリッククラスのロスハンボ{
public static <tは競合他社を拡張します<t >> void match(t a、t b){
System.out.println(a + "vs." + b + ":" + a.compete(b));
}
public static <tはenum <t>&競合他社<t >> void play(class <t> rsbclass、int size){
for(int i = 0; i <size; i ++)
マッチ(enums.random(rsbclass)、enums.random(rsbclass));
}
} ///:〜
静的メソッドを策定すると、列挙タイプごとに異なる方法が提供されるため、複数の開梱を実現するための優れたソリューションのように思われますが、列挙インスタンスがタイプではないという問題に直面しているため、Switchステートメントを追加することだけです:
次のようにコードをコピーします。
//:Enumerated/Roshambo3.java
//定数固有の方法を使用します。
パッケージが列挙されています。
Static Enumerated.outcome。*;
Public Enum Roshambo3は競争相手<roshambo3> {{
紙 {
パブリック結果は競合する(Roshambo3 IT){
switch(it){
デフォルト://コンパイラを配置します
ケースペーパー:抽選を返します。
ケースハサミ:リターンロス;
ケースロック:リターンウィン;
}
}
}、
はさみ{
パブリック結果は競合する(Roshambo3 IT){
switch(it){
デフォルト:
ケースペーパー:復帰勝利。
ケースハサミ:抽選を返します。
ケースロック:リターンロス;
}
}
}、
ロック {
パブリック結果は競合する(Roshambo3 IT){
switch(it){
デフォルト:
ケースペーパー:リターンロス;
ケースハサミ:返品勝利。
ケースロック:抽選を返します。
}
}
};
パブリックアブストラクトの結果は競合します(Roshambo3 IT);
public static void main(String[] args) {
Roshambo.play(roshambo3.class、20);
}
}/ * Roshambo2.javaと同じ出力 * ///:〜
次のコードは、より簡潔な実装方法です。
次のようにコードをコピーします。
//:Enumerated/Roshambo4.java
パッケージが列挙されています。
Public Enum Roshambo4は競争相手<roshambo4> {{
ロック {
パブリック結果競合(Roshambo4の対戦相手){
Return Compere(はさみ、相手);
}
}、
はさみ{
パブリック結果競合(Roshambo4の対戦相手){
競合を返す(紙、相手);
}
}、
紙 {
パブリック結果競合(Roshambo4の対戦相手){
Return Compere(Rock、敵);
}
};
結果は競争します(roshambo4 loser、roshambo4の対戦相手){
return((opponent == this)?outsome.draw:((opponent == loser)?outcome.win:outsom.lose));
}
public static void main(String[] args) {
Roshambo.play(roshambo4.class、20);
}
}/ * Roshambo2.javaと同じ出力 * ///:〜
Enummapクラスは、実際に複数の開梱を実装する良い方法のようです。
次のようにコードをコピーします。
//:Enumerated/Roshambo5.java
// enummapの列挙を使用した複数のディスパッチ。
パッケージが列挙されています。
java.util.* をインポートします。
Static Enumerated.outcome。*;
Enum Roshambo5は競合他社を実装しています<Roshambo5> {{
紙、はさみ、岩;
static enummap <roshambo5、enummap <roshambo5、outsome >> table = new enummap <roshambo5、enummap <roshambo5、outsome >>(roshambo5.class);
静的 {
for(roshambo5 it:roshambo5.values())
table.put(it、new enummap <roshambo5、outcome>(roshambo5.class));
initrow(紙、描画、負け、勝利);
initrow(はさみ、勝ち、引き分け、失う);
initrow(ロック、負け、勝ち、描画);
}
static void initrow(roshambo5 it、out bpaper、out vscissors、out brock){
enummap <roshambo5、outsome> row = roshambo5.table.get(it);
row.put(roshambo5.paper、vpaper);
row.put(roshambo5.scissors、vscissors);
row.put(roshambo5.rock、vrock);
}
パブリックアウトカム競合(Roshambo5 IT){
return table.get(this).get(it);
}
public static void main(String[] args) {
Roshambo.play(roshambo5.class、20);
}
}/ * Roshambo2.javaと同じ出力 * ///:〜
また、固定値を持つ列挙インスタンスの機能を使用して、最も単純な実装方法にデータを使用することもできます。
次のようにコードをコピーします。
//:Enumerated/Roshambo6.java
//複数のディスパッチの代わりに「テーブル」を使用した酵素。
パッケージが列挙されています。
Static Enumerated.outcome。*;
Enum Roshambo6は競争相手<Roshambo6> {{
紙、はさみ、岩;
プライベート静的結果[] []テーブル= {
{描画、負け、勝ち}、//ペーパー
{win、draw、lose}、//はさみ
{lose、win、draw}、//ロック
};
パブリック結果競合(Roshambo6 other){
return table [this.ordinal()] [other.ordinal()];
}
public static void main(String[] args) {
Roshambo.play(roshambo6.class、20);
}
} ///:〜