1. Trang chủ
  2. » Công Nghệ Thông Tin

Thinking in Java 4th Edition phần 8 pot

108 303 0

Đang tải... (xem toàn văn)

Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống

THÔNG TIN TÀI LIỆU

Thông tin cơ bản

Định dạng
Số trang 108
Dung lượng 1,37 MB

Nội dung

 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 of the above enums takes the corresponding Class object as a constructor argument, from which it can extract and store all the enum instances using getEnumConstants( ). These instances are later used in randomSelection( ), so now we can create a randomly generated meal by selecting one Food item from each Course: //: 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 *///:~ Enumerated Types 735 Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com  In this case, the value of creating an enum of enums is to iterate through each Course. Later, in the VendingMachine.java example, you’ll see another approach to categorization which is dictated by different constraints. Another, more compact, approach to the problem of categorization is to nest enums within enums, like this: //: 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 *///:~ The Security interface is necessary to collect the contained enums together as a common type. These are then categorized into the enums within SecurityCategory. If we take this approach with the Food example, the result is: //: 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) { 736 Thinking in Java Bruce Eckel Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com  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 *///:~ In the end, it’s only a reorganization of the code but it may produce a clearer structure in some cases. Exercise 3: (1) Add a new Course to Course.java and demonstrate that it works in Meal.java. Exercise 4: (1) Repeat the above exercise for Meal2.java. Exercise 5: (4) Modify control/VowelsAndConsonants.java so that it uses three enum types: VOWEL, SOMETIMES_A_VOWEL, and CONSONANT. The enum constructor should take the various letters that describe that particular category. Hint: Use varargs, and remember that varargs automatically creates an array for you. Exercise 6: (3) Is there any special benefit in nesting Appetizer, MainCourse, Dessert, and Coffee inside Food rather than making them standalone enums that just happen to implement Food? Using EnumSet instead of flags A Set is a kind of collection that only allows one of each type of object to be added. Of course, an enum requires that all its members be unique, so it would seem to have set behavior, but since you can’t add or remove elements it’s not very useful as a set. The EnumSet was added to Java SE5 to work in concert with enums to create a replacement for traditional int-based "bit flags." Such flags are used to indicate some kind of on-off information, but you end up manipulating bits rather than concepts, so it’s easy to write confusing code. Enumerated Types 737 Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com  The EnumSet is designed for speed, because it must compete effectively with bit flags (operations will be typically much faster than a HashSet). Internally, it is represented by (if possible) a single long that is treated as a bit-vector, so it’s extremely fast and efficient. The benefit is that you now have a much more expressive way to indicate the presence or absence of a binary feature, without having to worry about performance. The elements of an EnumSet must come from a single enum. A possible example uses an enum of positions in a building where alarm sensors are present: //: enumerated/AlarmPoints.java package enumerated; public enum AlarmPoints { STAIR1, STAIR2, LOBBY, OFFICE1, OFFICE2, OFFICE3, OFFICE4, BATHROOM, UTILITY, KITCHEN } ///:~ The EnumSet can be used to keep track of the alarm status: //: 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] *///:~ A static import is used to simplify the use of the enum constants. The method names are fairly self-explanatory, and you can find the full details in the JDK documentation. When you look at this documentation, you’ll see something interesting—the of( ) method has been overloaded both with varargs and with individual methods taking two through five explicit arguments. This is an indication of the concern for performance with EnumSet, because a single of( ) method using varargs could have solved the problem, but it’s slightly less efficient than having explicit arguments. Thus, if you call of( ) with two through five arguments you will get the explicit (slightly faster) method calls, but if you call it with one argument or more than five, you will get the varargs version of of( ). Notice that if you call it with one argument, the compiler will not construct the varargs array and so there is no extra overhead for calling that version with a single argument. 738 Thinking in Java Bruce Eckel Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com  EnumSets are built on top of longs, a long is 64 bits, and each enum instance requires one bit to indicate presence or absence. This means you can have an EnumSet for an enum of up to 64 elements without going beyond the use of a single long. What happens if you have more than 64 elements in your enum? //: 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] *///:~ The EnumSet clearly has no problem with an enum that has more than 64 elements, so we may presume that it adds another long when necessary. Exercise 7: (3) Find the source code for EnumSet and explain how it works. Using EnumMap An EnumMap is a specialized Map that requires that its keys be from a single enum. Because of the constraints on an enum, an EnumMap can be implemented internally as an array. Thus they are extremely fast, so you can freely use EnumMaps for enum-based lookups. You can only call put( ) for keys that are in your enum, but other than that it’s like using an ordinary Map. Here’s an example that demonstrates the use of the Command design pattern. This pattern starts with an interface containing (typically) a single method, and creates multiple implementations with different behavior for that method. You install Command objects, and your program calls them when necessary: //: 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 { Enumerated Types 739 Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com  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 *///:~ Just as with EnumSet, the order of elements in the EnumMap is determined by their order of definition in the enum. The last part of main( ) shows that there is always a key entry for each of the enums, but the value is null unless you have called put( ) for that key. One advantage of EnumMap over constant-specific methods (described next) is that an EnumMap allows you to change the value objects, whereas you’ll see that constant-specific methods are fixed at compile time. As you’ll see later in the chapter, EnumMaps can be used to perform multiple dispatching for situations where you have multiple types of enums interacting with each other. Constant-specific methods Java enums have a very interesting feature that allows you to give each enum instance different behavior by creating methods for each one. To do this, you define one or more abstract methods as part of the enum, then define the methods for each enum instance. For example: //: 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"); 740 Thinking in Java Bruce Eckel Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com  } }, 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) *///:~ You can look up and call methods via their associated enum instance. This is often called table-driven code (and note the similarity to the aforementioned Command pattern). In object-oriented programming, different behavior is associated with different classes. Because each instance of an enum can have its own behavior via constant-specific methods, this suggests that each instance is a distinct type. In the above example, each enum instance is being treated as the "base type" ConstantSpecificMethod but you get polymorphic behavior with the method call getInfo( ). However, you can only take the similarity so far. You cannot treat enum instances as class types: //: 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; *///:~ In f1( ), you can see that the compiler doesn’t allow you to use an enum instance as a class type, which makes sense if you consider the code generated by the compiler—each enum element is a static final instance of LikeClasses. Also, because they are static, enum instances of inner enums do not behave like ordinary inner classes; you cannot access non-static fields or methods in the outer class. As a more interesting example, consider a car wash. Each customer is given a menu of choices for their wash, and each option performs a different action. A constant-specific method can be associated with each option, and an EnumSet can be used to hold the customer’s selections: Enumerated Types 741 Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com  //: 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 *///:~ The syntax for defining a constant-specific method is effectively that of an anonymous inner class, but more succinct. 742 Thinking in Java Bruce Eckel Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com  This example also shows more characteristics of EnumSets. Since it’s a set, it will only hold one of each item, so duplicate calls to add( ) with the same argument are ignored (this makes sense, since you can only flip a bit "on" once). Also, the order that you add enum instances is unimportant—the output order is determined by the declaration order of the enum. Is it possible to override constant-specific methods, instead of implementing an abstract method? Yes, as you can see here: //: 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 *///:~ Although enums do prevent certain types of code, in general you should experiment with them as if they were classes. Chain of Responsibility with enums In the Chain of Responsibility design pattern, you create a number of different ways to solve a problem and chain them together. When a request occurs, it is passed along the chain until one of the solutions can handle the request. You can easily implement a simple Chain of Responsibility with constantspecific methods. Consider a model of a post office, which tries to deal with each piece of mail in the most general way possible, but has to keep trying until it ends up treating the mail as a dead letter. Each attempt can be thought of as a Strategy (another design pattern), and the entire list together is a Chain of Responsibility. We start by describing a piece of mail. All the different characteristics of interest can be expressed using enums. Because the Mail objects will be randomly generated, the easiest way to reduce the probability of (for example) a piece of mail being given a YES for GeneralDelivery is to create more non-YES instances, so the enum definitions look a little funny at first. Within Mail, you’ll see randomMail( ), which creates random pieces of test mail. The generator( ) method produces an Iterable object that uses randomMail( ) to produce a number of mail objects, one each time you call next( ) via the iterator. This construct allows the simple creation of a foreach loop by calling Mail.generator( ): //: enumerated/PostOffice.java // Modeling a post office. Enumerated Types 743 Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com  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; } 744 Thinking in Java Bruce Eckel Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com [...]... 1) continue; // Not a db table column if(anns[0] instanceof SQLInteger) { SQLInteger sInt = (SQLInteger) anns[0]; // Use field name if name not specified if(sInt.name().length() < 1) columnName = field.getName().toUpperCase(); else columnName = sInt.name(); columnDefs.add(columnName + " INT" + getConstraints(sInt.constraints())); } if(anns[0] instanceof SQLString) { SQLString sString = (SQLString) anns[0];... predefined in Java SE5, in general the kind of annotations you add and what you do with them are entirely up to you The syntax of annotations is reasonably simple and consists mainly of the addition of the @ symbol to the language Java SE5 contains three generalpurpose built -in annotations, defined in java. lang: • @Override, to indicate that a method definition is intended to override a method in the... have multiple instances of VendingMachine 750 Thinking in Java Bruce Eckel   Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com Exercise 11: (7) In a real vending machine you will want to easily add and change the type of vended items, so the limits imposed by an enum on Input are impractical (remember that enums are for a restricted set of types) Modify VendingMachine .java so that... the vended items are represented by a class instead of being part of Input, and initialize an Array List of these objects from a text file (using net.mindview.util.TextFile) Project 3 Design the vending machine using internationalization, so that one machine can easily be adapted to all countries Multiple dispatching When you are dealing with multiple interacting types, a program can get particularly... tested in two ways, by using two different Generator objects The RandomInputGenerator just keeps producing inputs, everything except SHUT_DOWN By running this for a long time you get a kind of sanity check to help ensure that the machine will not wander into a bad state The FilelnputGenerator takes a file describing inputs in text form, turns them into enum instances, and creates Input objects Here’s the... available in the class file by the compiler but can be discarded by the VM RUNTIME: Annotations are retained by the VM at run time, so they may be read reflectively @Documented Include this annotation in the Javadocs @Inherited Allow subclasses to inherit parent annotations Most of the time, you will be defining your own annotations and writing your own processors to deal with them   764 Thinking in Java. .. design is that the fields in VendingMachine that are accessed by enum State instances must be static, which means you can only have a single VendingMachine instance This may not be that big of an issue if you think about an actual (embedded Java) implementation, since you are likely to have only one application per machine Exercise 10: (7) Modify class VendingMachine (only) using EnumMap so that one... the VendingMachine by noting that in each State, you need to switch on the basic categories of input action: money being inserted, an item being selected, the transaction being aborted, and the machine being turned off However, within those categories, you have different types of money that can be inserted and different items that can be selected The Category enum groups the different types of Input... chaining in order to solve problems like this Using PostOffice .java for inspiration, research such languages and develop a program that allows new "rules" to be easily added to the system State machines with enums Enumerated types can be ideal for creating state machines A state machine can be in a finite number of specific states The machine normally moves from one state to the next based on an input,... manager can get an idea of project progress by counting the implemented use cases, and developers maintaining the project can easily find use cases if they need to update or debug business rules within the system 762 Thinking in Java Bruce Eckel   Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com //: annotations/UseCase .java import java. lang.annotation.*; @Target(ElementType.METHOD) . Applying hot wax Rinsing Blowing dry *///:~ The syntax for defining a constant-specific method is effectively that of an anonymous inner class, but more succinct. 742 Thinking in Java Bruce Eckel. not included in the solution guide. 4 This example existed for a number of years in both C++ and Java (in Thinking in Patterns) on www.MindView.net before it appeared, without attribution, in. vending machine is a good example of a state machine. First, we define the various inputs in an enum: //: enumerated/Input .java package enumerated; import java. util.*; public enum Input

Ngày đăng: 14/08/2014, 00:21

TỪ KHÓA LIÊN QUAN