Java 1.5 Tiger A Developers Notebook [Electronic resources]

David Flanagan, Brett McLaughlin

نسخه متنی -صفحه : 131/ 42
نمايش فراداده

3.6 Sets of Enums

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.

3.6.1 How do I do that?

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.

Example 3-5. Guitar feature set
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.

Example 3-6. Representing guitar features in Tiger
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);

This is really a bad ideareordering of the enum screws this up, and you can never really completely insulate yourself from that possibility. Always use of( ) instead of range( ), if at all possible.

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.