There is a values method in the enumeration that is used to generate an array in the order defined by the enumeration, which can be used to traverse. Our custom enumeration classes all inherit from java.lang.Enum and have the following functions in examples:
Copy the code code as follows:
//: enumerated/EnumClass.java
// Capabilities of the Enum class
import static net.mindview.util.Print.*;
enum Shrubbery { GROUND, CRAWLING, HANGING }
public class 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("----------------------");
}
// Produce an enum value from a string name:
for(String s : "HANGING CRAWLING GROUND".split(" ")) {
Shrubbery shrub = Enum.valueOf(Shrubbery.class, s);
print(shrub);
}
}
} /* Output:
GROUND ordinal: 0
-1 false false
class Shrubberry
Joshua Bloch was extremely helpful in developing this chapter.
GROUND
-----------------------
CRAWLING ordinal: 1
true true
class Shrubberry
CRAWLING
-----------------------
HANGING ordinal: 2
false false
class Shrubberry
HANGING
-----------------------
HANGING
CRAWLING
GROUND
*///:~
We can also use static enumeration references:
Copy the code code as follows:
//: enumerated/Spiciness.java
package enumerated;
public enum Spiciness {NOT, MILD, MEDIUM, HOT, FLAMING} ///:~
//: enumerated/Burrito.java
package enumerated;
import static enumerated.Spiciness.*;
public class Burrito {
Spiciness degree;
public Burrito(Spiciness degree) { this.degree = degree;}
public String toString() { return "Burrito is "+ degree;}
public static void main(String[] args) {
System.out.println(new Burrito(NOT));
System.out.println(new Burrito(MEDIUM));
System.out.println(new Burrito(HOT));
}
} /* Output:
Burrito is NOT
Burrito is MEDIUM
Burrito is HOT
*///:~
In addition to adding methods to an enumeration, which cannot be inherited, the enumeration can be treated as a general class, which means that you can add methods to the enumeration, and you can also define the main method in the enumeration:
Copy the code code as follows:
//: enumerated/OzWitch.java
// The witches in the land of Oz.
import static net.mindview.util.Print.*;
public enum OzWitch {
// Instances must be defined first, before methods:
WEST("Miss Gulch, aka the Wicked Witch of the West"),NORTH("Glinda, the Good Witch of the North"),EAST("Wicked Witch of the East, wearer of the Ruby " + "Slippers, crushed by Dorothy's house"),SOUTH("Good by inference, but missing");
private String description;
// Constructor must be package or private access:
private OzWitch(String description) {
this.description = description;
}
public String getDescription() { return description; }
public static void main(String[] args) {
for(OzWitch witch : OzWitch.values())
print(witch + ": " + witch.getDescription());
}
} /* Output:
WEST: Miss Gulch, aka the Wicked Witch of the West
NORTH: Glinda, the Good Witch of the North
EAST: Wicked Witch of the East, wearer of the Ruby Slippers, crushed by Dorothy's house
SOUTH: Good by inference, but missing
*///:~
Copy the code code as follows:
//: enumerated/SpaceShip.java
public enum SpaceShip {
SCOUT, CARGO, TRANSPORT, CRUISER, BATTLESHIP, MOTHERSHIP;
public String toString() {
String id = name();
String lower = id.substring(1).toLowerCase();
return id.charAt(0) + lower;
}
public static void main(String[] args) {
for(SpaceShip s : values()) {
System.out.println(s);
}
}
} /* Output:
Scout
Cargo
Transport
Cruiser
Battleship
Mothership
*///:~
Enumerations in switch statements An important role of enumerations is in switch statements. Usually switch statements only work on integer values, but there is a built-in order of integers in enumerations, so the order of instances can be determined by some method. Obtained, so enums can be used in switch statements:
Copy the code code as follows:
//: enumerated/TrafficLight.java
// Enums in switch statements.
import static net.mindview.util.Print.*;
// Define an enum type:
enum Signal { GREEN, YELLOW, RED, }
public class TrafficLight {
Signal color = Signal.RED;
public void change() {
switch(color) {
// Note that you don't have to say Signal.RED
// in the case statement:
case RED: color = Signal.GREEN;
break;
case GREEN: color = Signal.YELLOW;
break;
case YELLOW: color = Signal.RED;
break;
}
}
public String toString() {
return "The traffic light is " + color;
}
public static void main(String[] args) {
TrafficLight t = new TrafficLight();
for(int i = 0; i < 7; i++) {
print(t);
t.change();
}
}
} /* Output:
The traffic light is RED
The traffic light is GREEN
The traffic light is YELLOW
The traffic light is RED
The traffic light is GREEN
The traffic light is YELLOW
The traffic light is RED
*///:~
The secret of values() Although we used the values method before, if we look at Enum, we do not find the values method. So are there other hidden methods? We can check it out with a simple reflection code:
Copy the code code as follows:
//: enumerated/Reflection.java
// Analyzing enums using reflection.
import java.lang.reflect.*;
import java.util.*;
import net.mindview.util.*;
import static net.mindview.util.Print.*;
enum Explore { HERE, THERE }
public class Reflection {
public static Set<String> analyze(Class<?> enumClass) {
print("----- Analyzing " + enumClass + " -----");
print("Interfaces:");
for(Type t : enumClass.getGenericInterfaces())
print(t);
print("Base: " + enumClass.getSuperclass());
print("Methods: ");
Set<String> methods = new TreeSet<String>();
for(Method m : enumClass.getMethods())
methods.add(m.getName());
print(methods);
return methods;
}
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);
// Decompile the code for the enum:
OSExecute.command("javap Explore");
}
} /* Output:
----- Analyzing class Explore -----
Interfaces:
Base: class java.lang.Enum
Methods:
[compareTo, equals, getClass, getDeclaringClass, hashCode, name, notify, notifyAll, ordinal, toString, valueOf, values, wait]
----- Analyzing class java.lang.Enum -----
Interfaces:
java.lang.Comparable<E>
interface java.io.Serializable
Base: class java.lang.Object
Methods:
[compareTo, equals, getClass, getDeclaringClass, hashCode, name, notify, notifyAll, ordinal, toString, valueOf, wait]
Explore.containsAll(Enum)? true
Explore.removeAll(Enum): [values]
Compiled from "Reflection.java"
final class Explore extends java.lang.Enum{
public static final Explore HERE;
public static final Explore THERE;
public static final Explore[] values();
public static Explore valueOf(java.lang.String);
static {};
}
*///:~
We can see that the values method is added by the compiler. The valueOf method is also added by the compiler when creating the enumeration, but there is also a valueOf method in the Enum class, but this method has two parameters, while the valueOf method added by the compiler has only one parameter. Enumerations are interpreted as final by the compiler, so enumerations cannot be inherited. Because the values method is a static method added by the compiler, if you cast the enumeration to Enum, the values method will not be available, but there is a getEnumConstants method in Class, so although the values method is not available in Enum , but you can still get the enumeration instance through the Class object:
Copy the code code as follows:
//: enumerated/UpcastEnum.java
// No values() method if you upcast an enum
enum Search { HITHER, YON }
public class UpcastEnum {
public static void main(String[] args) {
Search[] vals = Search.values();
Enum e = Search.HITHER; // Upcast
// e.values(); // No values() in Enum
for(Enum en : e.getClass().getEnumConstants())
System.out.println(en);
}
} /* Output:
HITHER
YON
*///:~
Implementation without inheritance Because the enumeration types we define all inherit from java.lang.Enum, and Java does not support multiple inheritance, so enumerations are not created through inheritance, but enumerations can be created by inheriting one or more interfaces:
Copy the code code as follows:
//: enumerated/cartoons/EnumImplementation.java
// An enum can implement an interface
package enumerated.cartoons;
import java.util.*;
import net.mindview.util.*;
enum CartoonCharacter implements Generator<CartoonCharacter> {
SLAPPY, SPANKY, PUNCHY, SILLY, BOUNCY, NUTTY, BOB;
private Random rand = new Random(47);
public CartoonCharacter next() {
return values()[rand.nextInt(values().length)];
}
}
public class EnumImplementation {
public static <T> void printNext(Generator<T> rg) {
System.out.print(rg.next() + ", ");
}
public static void main(String[] args) {
// Choose any instance:
CartoonCharacter cc = CartoonCharacter.BOB;
for(int i = 0; i < 10; i++)
printNext(cc);
}
} /* Output:
BOB, PUNCHY, BOB, SPANKY, NUTTY, PUNCHY, SLAPPY, NUTTY, NUTTY, SLAPPY,
*///:~
Random Selection In many of our examples later, we will randomly select objects from enumeration instances. We create a public class to implement this:
Copy the code code as follows:
//: net/mindview/util/Enums.java
package net.mindview.util;
import java.util.*;
public class Enums {
private static Random rand = new Random(47);
public static <T extends Enum<T>> T random(Class<T> ec) {
return random(ec.getEnumConstants());
}
public static <T> T random(T[] values) {
return values[rand.nextInt(values.length)];
}
} ///:~
Copy the code code as follows:
//: enumerated/RandomTest.java
import net.mindview.util.*;
enum Activity { SITTING, LYING, STANDING, HOPPING, RUNNING, DODGING, JUMPING, FALLING, FLYING }
public class RandomTest {
public static void main(String[] args) {
for(int i = 0; i < 20; i++)
System.out.print(Enums.random(Activity.class) + " ");
}
} /* Output:
STANDING FLYING RUNNING STANDING RUNNING STANDING LYING DODGING SITTING RUNNING HOPPING HOPPING HOPPING RUNNING STANDING LYING FALLING RUNNING FLYING LYING
*///:~
Using interfaces to organize enumerations cannot be inherited, which sometimes causes us inconvenience, because sometimes we want to expand the number of enumerations through inheritance, and sometimes we need to group enumerations. For the latter, we can define grouped enumerations within the interface, and then create the enumeration by inheriting from this interface. As follows, we have different food categories that need to be created as enumerations, but we need to define each category as The types of Food are as follows:
Copy the code code as follows:
//: enumerated/menu/Food.java
// Subcategorization of enums within interfaces.
package enumerated.menu;
public interface Food {enum Appetizer implements Food {SALAD, SOUP, SPRING_ROLLS;}
enum MainCourse implements Food {LASAGNE, BURRITO, PAD_THAI,LENTILS, HUMMOUS, VINDALOO;}
enum Dessert implements Food {TIRAMISU, GELATO, BLACK_FOREST_CAKE,FRUIT, CREME_CARAMEL;}
enum Coffee implements Food {BLACK_COFFEE, DECAF_COFFEE, ESPRESSO,LATTE, CAPPUCCINO, TEA, HERB_TEA;}
} ///:~
Because each enumeration is defined as an implementation of an interface, each enumeration is of type Food, as follows:
Copy the code code as follows:
//: enumerated/menu/TypeOfFood.java
package enumerated.menu;
import static enumerated.menu.Food.*;
public class TypeOfFood {
public static void main(String[] args) {
Food food = Appetizer.SALAD;
food = MainCourse.LASAGNE;
food = Dessert.GELATO;
food = Coffee.CAPPUCCINO;
}
} ///:~
But interfaces cannot operate on multiple types like enumerations, so if you need an enumeration of enumerations, you can encapsulate an instance of each enumeration type in an enumeration:
Copy the code code as follows:
//: enumerated/menu/Course.java
package enumerated.menu;
import net.mindview.util.*;
public enum Course {
APPETIZER(Food.Appetizer.class),MAINCOURSE(Food.MainCourse.class),DESSERT(Food.Dessert.class),COFFEE(Food.Coffee.class);
private Food[] values;
private Course(Class<? extends Food> kind) {
values = kind.getEnumConstants();
}
public Food randomSelection() {
return Enums.random(values);
}
} ///:~
Each enumeration uses the Class object as the corresponding constructor parameter. We can use getEnumConstants from this parameter to obtain the enumeration instance. This instance can be used in the randomSelection method to generate random meals:
Copy the code code as follows:
//: enumerated/menu/Meal.java
package enumerated.menu;
public class Meal {
public static void main(String[] args) {
for(int i = 0; i < 5; i++) {
for(Course course : Course.values()) {
Food food = course.randomSelection();
System.out.println(food);
}
System.out.println("---");
}
}
} /* Output:
SPRING_ROLLS
VINDALOO
FRUIT
DECAF_COFFEE
---
SOUP
VINDALOO
FRUIT
TEA
---
SALAD
BURRITO
FRUIT
TEA
---
SALAD
BURRITO
CREME_CARAMEL
LATTE
---
SOUP
BURRITO
TIRAMISU
ESPRESSO
---
*///:~
Here's a more compact implementation:
Copy the code code as follows:
//: enumerated/SecurityCategory.java
// More succinct subcategorization of enums.
import net.mindview.util.*;
enum SecurityCategory {
STOCK(Security.Stock.class), BOND(Security.Bond.class);
Security[] values;
SecurityCategory(Class<? extends Security> kind) {
values = kind.getEnumConstants();
}
interface Security {
enum Stock implements Security { SHORT, LONG, MARGIN }
enum Bond implements Security { MUNICIPAL, JUNK }
}
public Security randomSelection() {
return Enums.random(values);
}
public static void main(String[] args) {
for(int i = 0; i < 10; i++) {
SecurityCategory category = Enums.random(SecurityCategory.class);
System.out.println(category + ": " +
category.randomSelection());
}
}
} /* Output:
BOND: MUNICIPAL
BOND: MUNICIPAL
STOCK: MARGIN
STOCK: MARGIN
BOND: JUNK
STOCK: SHORT
STOCK: LONG
STOCK: LONG
BOND: MUNICIPAL
BOND: JUNK
*///:~
Copy the code code as follows:
//: enumerated/menu/Meal2.java
package enumerated.menu;
import net.mindview.util.*;
public enum Meal2 {
APPETIZER(Food.Appetizer.class),MAINCOURSE(Food.MainCourse.class),DESSERT(Food.Dessert.class),COFFEE(Food.Coffee.class);
private Food[] values;
private Meal2(Class<? extends Food> kind) {
values = kind.getEnumConstants();
}
public interface Food {
enum Appetizer implements Food {SALAD, SOUP, SPRING_ROLLS;}
enum MainCourse implements Food {LASAGNE, BURRITO, PAD_THAI,LENTILS, HUMMOUS, VINDALOO;}
enum Dessert implements Food {TIRAMISU, GELATO, BLACK_FOREST_CAKE,FRUIT, CREME_CARAMEL;}
enum Coffee implements Food {BLACK_COFFEE, DECAF_COFFEE, ESPRESSO,LATTE, CAPPUCCINO, TEA, HERB_TEA;}
}
public Food randomSelection() {
return Enums.random(values);
}
public static void main(String[] args) {
for(int i = 0; i < 5; i++) {
for(Meal2 meal : Meal2.values()) {
Food food = meal.randomSelection();
System.out.println(food);
}
System.out.println("---");
}
}
} /* Same output as Meal.java *///:~
Use EnumSet instead of flags EnumSet was added in Java SE5 to combine enums and Sets to replace integer-based bit flags. Bit flags are usually used to indicate the switch of some kind of information, but in the code, bits are operated on rather than meaningful concepts, so they are not easy to understand. EnumSet is faster than bit flags. It uses long internally to represent a bit vector, and then you can use a more conceptual language to represent the switch of a certain bit without worrying about efficiency. The elements in EnumSet must come from the same enumeration. The following defines an enumeration of the alarm position:
Copy the code code as follows:
//: enumerated/AlarmPoints.java
package enumerated;
public enum AlarmPoints {STAIR1, STAIR2, LOBBY, OFFICE1, OFFICE2, OFFICE3,OFFICE4, BATHROOM, UTILITY, KITCHEN} ///:~
Then use the EnumSet class to track the status of the alarm:
Copy the code code as follows:
//: enumerated/EnumSets.java
// Operations on EnumSets
package enumerated;
import java.util.*;
import static enumerated.AlarmPoints.*;
import static net.mindview.util.Print.*;
public class EnumSets {
public static void main(String[] args) {
EnumSet<AlarmPoints> points = EnumSet.noneOf(AlarmPoints.class); // Empty set
points.add(BATHROOM);
print(points);
points.addAll(EnumSet.of(STAIR1, STAIR2, KITCHEN));
print(points);
points = EnumSet.allOf(AlarmPoints.class);
points.removeAll(EnumSet.of(STAIR1, STAIR2, KITCHEN));
print(points);
points.removeAll(EnumSet.range(OFFICE1, OFFICE4));
print(points);
points = EnumSet.complementOf(points);
print(points);
}
} /* Output:
[BATHROOM]
[STAIR1, STAIR2, BATHROOM, KITCHEN]
[LOBBY, OFFICE1, OFFICE2, OFFICE3, OFFICE4, BATHROOM, UTILITY]
[LOBBY, BATHROOM, UTILITY]
[STAIR1, STAIR2, OFFICE1, OFFICE2, OFFICE3, OFFICE4, KITCHEN]
*///:~
EnumSet is built on long type and has 64 bits, so what if our enumeration type exceeds this number?
Copy the code code as follows:
//: enumerated/BigEnumSet.java
import java.util.*;
public class 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);
}
} /* Output:
[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]
*///:~
We can see that the program is running normally, so it is very likely that a long type has been added internally to help accommodate the enumeration type using EnumMap.
EnumMap is a special type of Map. The value of its key can only be the type in the same enumeration. Because of this, EnumMap can be implemented internally through an array, which is very efficient.
Copy the code code as follows:
//: enumerated/EnumMaps.java
// Basics of EnumMaps.
package enumerated;
import java.util.*;
import static enumerated.AlarmPoints.*;
import static net.mindview.util.Print.*;
interface Command { void action(); }
public class EnumMaps {
public static void main(String[] args) {
EnumMap<AlarmPoints,Command> em = new EnumMap<AlarmPoints,Command>(AlarmPoints.class);
em.put(KITCHEN, new Command() {
public void action() { print("Kitchen fire!"); }
});
em.put(BATHROOM, new Command() {
public void action() { print("Bathroom alert!"); }
});
for(Map.Entry<AlarmPoints,Command> e : em.entrySet()) {
printnb(e.getKey() + ": ");
e.getValue().action();
}
try { // If there's no value for a particular key:
em.get(UTILITY).action();
} catch(Exception e) {
print(e);
}
}
} /* Output:
BATHROOM: Bathroom alert!
KITCHEN: Kitchen fire!
java.lang.NullPointerException
*///:~
Specific constant methods Java enumerations have a very interesting feature, that is, different behaviors can be defined for each enumeration instance. In order to achieve this, we define one or more abstract methods as part of the enumeration, and then define different behaviors for each enumeration instance. An enumeration instance definition method:
Copy the code code as follows:
//: enumerated/ConstantSpecificMethod.java
import java.util.*;
import 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");}};
abstract String getInfo();
public static void main(String[] args) {
for(ConstantSpecificMethod csm : values())
System.out.println(csm.getInfo());
}
} /* (Execute to see output) *///:~
The above code seems to be stuck that each enumeration element is a different element, and all elements inherit from the ConstantSpecificMethod base class, but we cannot really understand it this way, because we cannot treat the enumeration elements as types:
Copy the code code as follows:
//: enumerated/NotClasses.java
// {Exec: javap -c LikeClasses}
import static net.mindview.util.Print.*;
enum LikeClasses {WINKEN { void behavior() { print("Behavior1"); } },BLINKEN { void behavior() { print("Behavior2"); } },NOD { void behavior() { print("Behavior3") ; } };
abstract void behavior();
}
public class NotClasses {
// void f1(LikeClasses.WINKEN instance) {} // Nope
} /* Output:
Compiled from "NotClasses.java"
abstract class LikeClasses extends java.lang.Enum{
public static final LikeClasses WINKEN;
public static final LikeClasses BLINKEN;
public static final LikeClasses NOD;
...
*///:~
Consider another example. There is a car wash menu. Customers choose different services based on different menus. You can use specific constant methods to associate the menu with services, and use EnumSet to maintain the customer's choices, as follows:
Copy the code code as follows:
//: enumerated/CarWash.java
import java.util.*;
import static net.mindview.util.Print.*;
public class CarWash {
public enum Cycle {UNDERBODY {void action() { print("Spraying the underbody"); }},
WHEELWASH {void action() { print("Washing the wheels"); }},
PREWASH {void action() { print("Loosening the dirt"); }},
BASIC {void action() { print("The basic wash"); }},
HOTWAX {void action() { print("Applying hot wax"); }},
RINSE {void action() { print("Rinsing"); }},
BLOWDRY {void action() { print("Blowing dry"); }};
abstract void action();
}
EnumSet<Cycle> cycles = EnumSet.of(Cycle.BASIC, Cycle.RINSE);
public void add(Cycle cycle) { cycles.add(cycle); }
public void washCar() {
for(Cycle c : cycles)
c.action();
}
public String toString() { return cycles.toString(); }
public static void main(String[] args) {
CarWash wash = new CarWash();
print(wash);
wash.washCar();
// Order of addition is unimportant:
wash.add(Cycle.BLOWDRY);
wash.add(Cycle.BLOWDRY); // Duplicates ignored
wash.add(Cycle.RINSE);
wash.add(Cycle.HOTWAX);
print(wash);
wash.washCar();
}
} /* Output:
[BASIC, RINSE]
The basic wash
Rinsing
[BASIC, HOTWAX, RINSE, BLOWDRY]
The basic wash
Applying hot wax
Rinsing
Blowing dry
*///:~
We can also override the default specific constant methods instead of using inherited abstract methods, as follows:
Copy the code code as follows:
//: enumerated/OverrideConstantSpecific.java
import static net.mindview.util.Print.*;
public enum OverrideConstantSpecific {
NUT, BOLT,WASHER {void f() { print("Overridden method"); }};
void f() { print("default behavior"); }
public static void main(String[] args) {
for(OverrideConstantSpecific ocs : values()) {
printnb(ocs + ": ");
ocs.f();
}
}
} /* Output:
NUT: default behavior
BOLT: default behavior
WASHER: Overridden method
*///:~
Sometimes we want to pass specific requests in the chain. Knowing that an object in the chain can handle the request can be easily achieved by using specific constant methods. The following is an example of processing emails:
Copy the code code as follows:
//: enumerated/PostOffice.java
// Modeling a post office.
Enumerated Types 743
import java.util.*;
import net.mindview.util.*;
import static net.mindview.util.Print.*;
class Mail {
// The NO's lower the probability of random selection:
enum GeneralDelivery {YES,NO1,NO2,NO3,NO4,NO5}
enum Scannability {UNSCANNABLE,YES1,YES2,YES3,YES4}
enum Readability {ILLEGIBLE,YES1,YES2,YES3,YES4}
enum Address {INCORRECT,OK1,OK2,OK3,OK4,OK5,OK6}
enum ReturnAddress {MISSING,OK1,OK2,OK3,OK4,OK5}
GeneralDelivery generalDelivery;
Scannability scannability;
Readability readability;
Address address;
ReturnAddress returnAddress;
static long counter = 0;
long id = counter++;
public String toString() { return "Mail " + id; }
public String details() {
return toString() + ", General Delivery: " + generalDelivery + ", Address Scanability: " + scannability + ", Address Readability: " + readability +
", Address Address: " + address + ", Return address: " + returnAddress;
}
//Generate test Mail:
public static Mail randomMail() {
Mail 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);
return m;
}
public static Iterable<Mail> generator(final int count) {
return new Iterable<Mail>() {
int n = count;
public Iterator<Mail> iterator() {
return new Iterator<Mail>() {
public boolean hasNext() { return n-- > 0; }
public Mail next() { return randomMail(); }
public void remove() { // Not implemented
throw new UnsupportedOperationException();
}
};
}
};
}
}
public class PostOffice {
enum MailHandler {
GENERAL_DELIVERY {
boolean handle(Mail m) {
switch(m.generalDelivery) {
case YES:
print("Using general delivery for " + m);
return true;
default: return false;
}
}
},
MACHINE_SCAN {
boolean handle(Mail m) {
switch(m.scannability) {
case UNSCANNABLE: return false;
default:
switch(m.address) {
case INCORRECT: return false;
default:
print("Delivering "+ m + " automatically");
return true;
}
}
}
},
VISUAL_INSPECTION {
boolean handle(Mail m) {
switch(m.readability) {
case ILLEGIBLE: return false;
default:
switch(m.address) {
case INCORRECT: return false;
default:
print("Delivering " + m + " normally");
return true;
}
}
}
},
RETURN_TO_SENDER {
boolean handle(Mail m) {
switch(m.returnAddress) {
case MISSING: return false;
default:
print("Returning " + m + " to sender");
return true;
}
}
};
abstract boolean handle(Mail m);
}
static void handle(Mail m) {
for(MailHandler handler : MailHandler.values())
if(handler.handle(m))
return;
print(m + " is a dead letter");
}
public static void main(String[] args) {
for(Mail mail : Mail.generator(10)) {
print(mail.details());
handle(mail);
print("*****");
}
}
} /* Output:
Mail 0, General Delivery: NO2, Address Scanability: UNSCANNABLE, Address Readability: YES3, Address Address: OK1, Return address: OK1
Delivering Mail 0 normally
*****
Mail 1, General Delivery: NO5, Address Scanability: YES3, Address Readability: ILLEGIBLE, Address Address: OK5, Return address: OK1
Delivering Mail 1 automatically
*****
Mail 2, General Delivery: YES, Address Scanability: YES3, Address Readability: YES1, Address Address: OK1, Return address: OK5
Using general delivery for Mail 2
*****
Mail 3, General Delivery: NO4, Address Scanability: YES3, Address Readability: YES1, Address Address: INCORRECT, Return address: OK4
Returning Mail 3 to sender
*****
Mail 4, General Delivery: NO4, Address Scanability: UNSCANNABLE, Address Readability: YES1, Address Address: INCORRECT, Return address: OK2
Returning Mail 4 to sender
*****
Mail 5, General Delivery: NO3, Address Scanability: YES1, Address Readability: ILLEGIBLE, Address Address: OK4, Return address: OK2
Delivering Mail 5 automatically
*****
Mail 6, General Delivery: YES, Address Scanability: YES4, Address Readability: ILLEGIBLE, Address Address: OK4, Return address: OK4
Using general delivery for Mail 6
*****
Mail 7, General Delivery: YES, Address Scanability: YES3, Address Readability: YES4, Address Address: OK2, Return address: MISSING
Using general delivery for Mail 7
*****
Mail 8, General Delivery: NO3, Address Scanability: YES1, Address Readability: YES3, Address Address: INCORRECT, Return address: MISSING
Mail 8 is a dead letter
*****
Mail 9, General Delivery: NO1, Address Scanability: UNSCANNABLE, Address Readability: YES2, Address Address: OK1, Return address: OK4
Delivering Mail 9 normally
*****
*///:~
The enumeration type is also an ideal type for creating a state machine. A state machine can move between a limited number of states according to the input, and then end the work after a certain state is met. In addition, each state will have a corresponding output. A vending machine is An example of a typical state machine where we define different inputs within an enumeration:
Copy the code code as follows:
//: enumerated/Input.java
package enumerated;
import java.util.*;
public enum Input {
NICKEL(5), DIME(10), QUARTER(25), DOLLAR(100),TOOTHPASTE(200), CHIPS(75), SODA(100), SOAP(50),ABORT_TRANSACTION {
public int amount() { // Disallow
throw new RuntimeException("ABORT.amount()");
}
},
STOP { // This must be the last instance.
public int amount() { // Disallow
throw new RuntimeException("SHUT_DOWN.amount()");
}
};
int value; // In cents
Input(int value) { this.value = value; }
Input() {}
int amount() { return value; }; // In cents
static Random rand = new Random(47);
public static Input randomSelection() {
// Don't include STOP:
return values()[rand.nextInt(values().length - 1)];
}
} ///:~
VendingMachine is used to respond to the input. First, it classifies the input through Category enumeration, and then uses the switch statement:
Copy the code code as follows:
//: enumerated/VendingMachine.java
// {Args: VendingMachineInput.txt}
package enumerated;
import java.util.*;
import net.mindview.util.*;
import static enumerated.Input.*;
import static net.mindview.util.Print.*;
enum Category {
MONEY(NICKEL, DIME, QUARTER, DOLLAR),ITEM_SELECTION(TOOTHPASTE, CHIPS, SODA, SOAP),QUIT_TRANSACTION(ABORT_TRANSACTION),SHUT_DOWN(STOP);
private Input[] values;
Category(Input... types) { values = types; }
private static EnumMap<Input,Category> categories = new EnumMap<Input,Category>(Input.class);
static {
for(Category c : Category.class.getEnumConstants())
for(Input type : c.values)
categories.put(type, c);
}
public static Category categorize(Input input) {
return categories.get(input);
}
}
public class VendingMachine {
private static State state = State.RESTING;
private static int amount = 0;
private static Input selection = null;
enum StateDuration {TRANSIENT} //Tagging enum
enum State {
RESTING {
void next(Input input) {
switch(Category.categorize(input)) {
case MONEY:
amount += input.amount();
state = ADDING_MONEY;
break;
case SHUT_DOWN:
state = TERMINAL;
default:
}
}
},
ADDING_MONEY {
void next(Input input) {
switch(Category.categorize(input)) {
case MONEY:
amount += input.amount();
break;
case ITEM_SELECTION:
selection = input;
if(amount < selection.amount())
print("Insufficient money for " + selection);
else state = DISPENSING;
break;
case QUIT_TRANSACTION:
state = GIVING_CHANGE;
break;
case SHUT_DOWN:
state = TERMINAL;
default:
}
}
},
DISPENSING(StateDuration.TRANSIENT) {
void next() {
print("here is your " + selection);
amount -= selection.amount();
state = GIVING_CHANGE;
}
},
GIVING_CHANGE(StateDuration.TRANSIENT) {
void next() {
if(amount > 0) {
print("Your change: " + amount);
amount = 0;
}
state = RESTING;
}
},
TERMINAL { void output() { print("Halted"); } };
private boolean isTransient = false;
State() {}
State(StateDuration trans) { isTransient = true; }
void next(Input input) {
throw new RuntimeException("Only call " + "next(Input input) for non-transient states");
}
void next() {
throw new RuntimeException("Only call next() for " + "StateDuration.TRANSIENT states");
}
void output() { print(amount); }
}
static void run(Generator<Input> gen) {
while(state != State.TERMINAL) {
state.next(gen.next());
while(state.isTransient)
state.next();
state.output();
}
}
public static void main(String[] args) {
Generator<Input> gen = new RandomInputGenerator();
if(args. length == 1)
gen = new FileInputGenerator(args[0]);
run(gen);
}
}
// For a basic sanity check:
class RandomInputGenerator implements Generator<Input> {
public Input next() { return Input.randomSelection(); }
}
// Create Inputs from a file of ';'-separated strings:
class FileInputGenerator implements Generator<Input> {
private Iterator<String> input;
public FileInputGenerator(String fileName) {
input = new TextFile(fileName, ";").iterator();
}
public Input next() {
if(!input.hasNext())
return null;
return Enum.valueOf(Input.class, input.next().trim());
}
} /* Output:
here is your CHIPS
here is your TOOTHPASTE
Your change: 35
Insufficient money for SODA
Insufficient money for SODA
Your change: 75
Halted
*///:~
Here is the test data used to generate the above output:
Copy the code code as follows:
QUARTER; QUARTER; QUARTER; CHIPS;
DOLLAR; DOLLAR; TOOTHPASTE;
QUARTER; DIME; ABORT_TRANSACTION;
QUARTER; DIME; SODA;
QUARTER; DIME; NICKEL; SODA;
ABORT_TRANSACTION;
STOP;
///:~
Multiple unpacking When dealing with interactions between multiple types, the code is likely to become messy, such as Number.plush(Number), Number.mutiply(Number), etc. Number is just a base class of the family, so when you call When a.plus(b), you neither know the type of a nor the type of b, so how to ensure that the interaction between them is correct? Java can only perform single unpacking, that is, if one or more operations of multiple unknown types are performed, Java can only perform a dynamic binding mechanism on one of the types, which cannot solve the problem we talked about above. , so you have to manually write the dynamic binding code.
The solution is to use multibinding. Polymorphism can only occur when calling a method, so if you need multiple unpacking, you must call multiple methods. With multiple unwrapping, you must have a virtual method that calls each type's method to unwrap. The following example is a rock-paper-scissors example:
Copy the code code as follows:
//: enumerated/Outcome.java
package enumerated;
public enum Outcome { WIN, LOSE, DRAW } ///:~
//: enumerated/RoShamBo1.java
// Demonstration of multiple dispatching.
package enumerated;
import java.util.*;
import static enumerated.Outcome.*;
interface Item {
Outcome compete(Item it);
Outcome eval(Paper p);
Outcome eval(Scissors);
Outcome eval(Rock r);
}
class Paper implements Item {
public Outcome compete(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 "Paper"; }
}
class Scissors implements Item {
public Outcome compete(Item it) { return it.eval(this); }
public Outcome eval(Paper p) { return LOSE; }
public Outcome eval(Scissors) { return DRAW; }
public Outcome eval(Rock r) { return WIN; }
public String toString() { return "Scissors"; }
}
class Rock implements Item {
public Outcome compete(Item it) { return it.eval(this); }
public Outcome eval(Paper p) { return WIN; }
public Outcome eval(Scissors s) { return LOSE; }
public Outcome eval(Rock r) { return DRAW; }
public String toString() { return "Rock"; }
}
public class RoShamBo1 {
static final int SIZE = 20;
private static Random rand = new Random(47);
public static Item newItem() {
switch(rand.nextInt(3)) {
default:
case 0: return new Scissors();
case 1: return new Paper();
case 2: return new Rock();
}
}
public static void match(Item a, Item 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());
}
} /* Output:
Rock vs. Rock: DRAW
Paper vs. Rock: WIN
Paper vs. Rock: WIN
Paper vs. Rock: WIN
Scissors vs. Paper: WIN
Scissors vs. Scissors: Draw
Scissors vs. Paper: Win
Rock vs. Paper: lose
Paper vs. Paper: Draw
Rock vs. Paper: lose
Paper vs. scissors: lose
Paper vs. scissors: lose
Rock vs. scissors: win
Rock vs. Paper: lose
Paper vs. Rock: Win
Scissors vs. Paper: Win
Paper vs. scissors: lose
Paper vs. scissors: lose
Paper vs. scissors: lose
Paper vs. scissors: lose
*///: ~
We have used many means to achieve multiple seals, but we have obtained a good code structure. The biggest problem when using an enumeration solution to implement the above code is that the enumeration instance is not a type, so the enumeration instance cannot be used as a parameter type. However, we can still have other methods to around the dog's obstacle. One method is to use the constructor to initialize each enumeration type, and then call the output organization a search table:
Copy the code code as follows:
//: ENUMERATED/ROSHAMBO2.java
// switching one enum on another.
package enumerated;
Import static enumerated.outcome.*;
public enum roshambo2 Implements Competitor <Roshambo2> {{{
Paper (drawer (draw, lose, win), scissors (winrsors (win, draw, lose), rock (lose, win, draw);
Private Outcom VPAPER, VSCISSORS, VROCK;
Roshambo2 (Outcom Paper, Outcom scissors, outcom rock) {
this.VPAPER = Paper;
this.vscissors = scissors;
this.vrock = rock;
}
pubble outcome compete (Roshambo2 it) {{
switch (it) {
default:
Case Paper: Return vpaper;
case scissors: Return vSCISSORS;
Case Rock: Return vrock;
}
}
public static void main(String[] args) {
Roshambo.play (Roshambo2.class, 20); 20); 20);
}
} /* Output:
Rock vs. Rock: Draw
Scissors vs. Rock: lose
Scissors vs. Rock: lose
Enumerated Types 753
Scissors vs. Rock: lose
Paper vs. scissors: lose
Paper vs. Paper: Draw
Paper vs. scissors: lose
Rock vs. scissors: win
Scissors vs. Scissors: Draw
Rock vs. scissors: win
Scissors vs. Paper: Win
Scissors vs. Paper: Win
Rock vs. Paper: lose
Rock vs. scissors: win
Scissors vs. Rock: lose
Paper vs. scissors: lose
Scissors vs. Paper: Win
Scissors vs. Paper: Win
Scissors vs. Paper: Win
Scissors vs. Paper: Win
*///: ~
Copy the code code as follows:
//: enumerated/competitor.java
// switching one enum on another.
package enumerated;
Public Interface Competitor <T Extends Competitor <T >> {
Outcom Compete (T Competitor);
} ///: ~
Copy the code code as follows:
//: enumerated/roshambo.java
// Common Tools for Roshambo Examples.
package enumerated;
import network.mindView.util.*;
public class roshambo {
Public Static <T Extends Competitor <T >> Void Match (T A, T B) {
System.out.println (a + "vs." + b + ":" + a.compete (b));
}
Public Static <T Exum Enum <t> & Competitor <T >> Void Play (Class <T> RSBClass, INT SIZE) {
for (int i = 0; i <size; i ++)
match (enums.random (RSBCLASS), enums.random (RSBClass));
}
} ///: ~
Because formulating a static method can provide different methods for each enumeration type, it looks like a good solution to achieve multiple sealing, but still facing a problem that is not a type of enumeration instance, so what we can do is to add a Switch Statement:
Copy the code code as follows:
//: ENUMERATED/ROSHAMBO3.java
// USING Constant-Specific Methods.
package enumerated;
Import static enumerated.outcome.*;
public enum Roshambo3 Implements Competitor <Roshambo3> {{{
Paper {
pubble outCome Compete (Roshambo3 it) {{
switch (it) {
default: // to placate the compiler
Case Paper: Return Draw;
case scissors: Return lose;
Case Rock: Return Win;
}
}
},
Scissors {
pubble outCome Compete (Roshambo3 it) {{
switch (it) {
default:
Case Paper: Return Win;
case scissors: Return draw;
Case Rock: Return Lose;
}
}
},
Rock {
pubble outCome Compete (Roshambo3 it) {{
switch (it) {
default:
Case Paper: Return Lose;
case scissors: Return win;
Case Rock: Return Draw;
}
}
};
Public Abstract Outcom Compete (Roshambo3 IT);
public static void main(String[] args) {
Roshambo.play (Roshambo3.class, 20); 20);
}
}/ * Same output as roshambo2.java * ///: ~
The following code is a more concise implementation method:
Copy the code code as follows:
//: ENUMERATED/ROSHAMBO4.java
package enumerated;
public enum Roshambo4 Implements Competitor <Roshambo4> {{{
Rock {
pblic outCome Compete (Roshambo4 OPPONENT) {{
Return Compete (Scissors, OPPONENT);
}
},
Scissors {
pblic outCome Compete (Roshambo4 OPPONENT) {{
Return Compete (Paper, OPPONENT);
}
},
Paper {
pblic outCome Compete (Roshambo4 OPPONENT) {{
Return Compete (ROCK, OPPONENT);
}
};
Outcom Compete (Roshambo4 Loser, Roshambo4 Opponent) {{
Return ((opponent == this)? Outcom.draw: ((opponent == loser)? Outcom.win: Outcom.lose));
}
public static void main(String[] args) {
Roshambo.play (Roshambo4.class, 20); 20);
}
}/ * Same output as roshambo2.java * ///: ~
ENUMMAP seems to be a good job that can really achieve multiple seals:
Copy the code code as follows:
//: ENUMERATED/ROSHAMBO5.java
// Multiple dispatching using an enummap of enummaps.
package enumerated;
import java.util.*;
Import static enumerated.outcome.*;
enum Roshambo5 Implements Competitor <Roshambo5> {{
Paper, scissors, rock;
Static Enummap <Roshambo5, Enummap <Roshambo5, Outcom >> Table = New Enummap <Roshambo5, Enummap <Roshambo5, Outcom >> (Roshambo5.class); (Roshambo5.class);
static {
for (rahambo5 it: roshambo5.values ())
table.put (it, new enummap <Roshambo5, Outcom> (Roshambo5.class));
Initrow (Paper, Draw, Lose, Win);
Initrow (scissors, win, draw, lose);
Initrow (ROCK, LOSE, Win, Draw);
}
Static Void InitroW (Roshambo5 It, Outcom VPAPER, Outcom VSCISSORS, OUTCOME VROCK) {{
Enummap <Roshambo5, Outcom> ROW = Roshambo5.table.get (IT);
row.put (Roshambo5.paper, VPAPER);
row.put (Roshambo5.Scissors, vscissors);
row.put (Roshambo5.Rock, VROCK);
}
pubble outCome Compete (Roshambo5 it) {{
Return table.get (this) .get (it);
}
public static void main(String[] args) {
Roshambo.play (Roshambo5.class, 20); 20); 20);
}
}/ * Same output as roshambo2.java * ///: ~
We can also use the characteristics of fixed values to use data to use data for the simplest implementation method. Here is a two -dimensional array for mapping to achieve:
Copy the code code as follows:
//: ENUMERATED/ROSHAMBO6.java
// ENUMS USING "TABLS" Instead of Multiple Dispatch.
package enumerated;
Import static enumerated.outcome.*;
enum Roshambo6 iMplements Competitor <Roshambo6> {{
Paper, scissors, rock;
Private Static Outcom [] [] table = {
{Draw, Lose, Win}, // Paper
{Win, Draw, Lose}, // Scissors
{Lose, Win, Draw}, // ROCK
};
public outCome Compete (Roshambo6 Other) {{
Return table [this.ordinal ()] [Other.ordinal ()];
}
public static void main(String[] args) {
Roshambo.play (Roshambo6.class, 20); 20);
}
} ///: ~