Another common usage of constants is to represent a feature set of a particular item, such as a car, or even something really exciting, like a guitar. You could then use bitwise operators to compare or check for specific features. As in the lab on Maps of Enums, the Java language folks realized this, and added another collection class for the purpose, java.util.EnumSet.
First, examine the way this scenario might be handled in pre-Tiger days. Example 3-5 is a simple class that defines some common guitar features.
package com.oreilly.tiger.ch03; public class OldGuitarFeatures { public static final int ROSEWOOD = 0x01; // back/sides public static final int MAHOGANY = 0x02; // back/sides public static final int ZIRICOTE = 0x04; // back/sides public static final int SPRUCE = 0x10; // top public static final int CEDAR = 0x12; // top public static final int AB_ROSETTE = 0x30; // abalone rosette public static final int AB_TOP_BORDER = 0x32; // abalone top border public static final int IL_DIAMONDS = 0x40; // diamond/square inlay public static final int IL_DOTS = 0x42; // dots inlays }
These are all features, and are all represented by powers of two. That allows them to be combined like this:
int bourgeoisD150 = OldGuitarFeatures.ROSEWOOD | OldGuitarFeatures.SPRUCE | OldGuitarFeatures.AB_ROSETTE | OldGuitarFeatures.IL_DIAMONDS;
With this initial work done (you could represent every guitar in a line this way), you can test a specific guitar for a specific features, using the bitwise AND operator:
boolean hasAbRosette = (bourgeoisD150 & OldGuitarFeatures.IL_DIAMONDS) != 0;
Looking at the constants in OldGuitarFeatures, you should see that they are just another case of an enumerated type, and could be represented in Tiger as shown in Example 3-6.
package com.oreilly.tiger.ch03; public enum GuitarFeatures { ROSEWOOD, // back/sides MAHOGANY, // back/sides ZIRICOTE, // back/sides SPRUCE, // top CEDAR, // top AB_ROSETTE, // abalone rosette AB_TOP_BORDER, // abalone top border IL_DIAMONDS, // diamond/square inlay IL_DOTS // dots inlays }
NOTE
Be sure and continue to compile with the "-source 1.5" switch. Using the provided Ant scripts takes care of this, by the way.
However, operating on these constants with bitwise operators isn't possible, at least without the help of a new class, java.util.EnumSet. Here are the methods of this class you should be concerned with, most of which are factories:
// Returns a new EnumSet with all elements from the supplied type public static EnumSet allOf(Class elementType); // Returns a new EnumSet of the same type as the supplied set, but // with all the values not in the supplied set; a mirror image public static EnumSet complementOf(EnumSet e); // Returns a new EnumSet from the provided collection public static EnumSet copyOf(Collection c); // Returns a new EnumSet with no values in it public static EnumSet noneOf(Class elementType); // Various methods to create an EnumSet with the supplied elements in it public static EnumSet of(E e[, E e2, E e3, E e4, E e5]); //Varags version public static EnumSet of(E... e); // Creates an EnumSet with a range of values public static EnumSet range(E from, E to); // returns a copy of the current set - not a factory method public EnumSet clone( );
You may have to look at this for a minute to get the sense of thingsthe format is rather odd. But, to create a new EnumSet of all guitar features, just use the following:
EnumSet allFeatures = EnumSet.allOf(GuitarFeatures);
Simple enough, right? If you want only the back and side woods, you could use this notation:
EnumSet backSides = EnumSet.of(GuitarFeatures.ROSEWOOD, GuitarFeatures.MAHOGANY, GuitarFeatures.ZIRICOTE);
You could also use the range( ) operator:
EnumSet backSides = EnumSet.range(GuitarFeatures.ROSEWOOD, GuitarFeatures.ZIRICOTE);
|
complentOf( ) is also a handy method:
EnumSet noAbalone = EnumSet.complementOf( EnumSet.of(GuitarFeatures.AB_ROSETTE, GuitarFeatures.AB_TOP_BORDER));
With your setup done, you can just use the contains( ) method, available to all collection classes, to check for a value or values:
EnumSet bourgeoisD150 = EnumSet.of(GuitarFeatures.ROSEWOOD, GuitarFeatures.SPRUCE, GuitarFeatures.AB_ROSETTE, GuitarFeatures.IL_DIAMONDS); boolean hasAbRosette = bourgeoisD150.contains(GuitarFeatures.AB_ROSETTE);
NOTE
You can't use varargs to work with the of( ) method, due to the usage of generic types in the factory method. Variable arguments are detailed in Chapter 5.