If you are stuck with Java 1.4 or lower, you can use Commons Lang
Enum
, ValuedEnum
, and
EnumUtils
to create an enum
“type,” which approximates the new
language feature. Example 1-10 extends Enum,
providing three flavors: Flavor.CHOCOLATE
,
Flavor.VANILLA
, and
Flavor.STRAWBERRY
.
Example 1-10. Defining a Flavor enumeration by extending Enum
import java.util.*; import org.apache.commons.lang.enum.Enum; public final class Flavor extends Enum { public static Flavor CHOCOLATE = new Flavor( "Chocolate" ); public static Flavor VANILLA = new Flavor( "Vanilla" ); public static Flavor STRAWBERRY = new Flavor( "Strawberry" ); private Flavor(String name, int value) { super( name, value ); } public static Flavor getEnum(String flavor) { return (Flavor) getEnum(Flavor.class, flavor); } public static Map getEnumMap( ) { return getEnumMap(Flavor.class); } public static List getEnumList( ) { return getEnumList(Flavor.class); } public static Iterator iterator( ) { return iterator(Flavor.class); } }
The key to an Enum
is the private constructor;
this guarantees that there are only three instances of
Flavor
in any virtual machine. This extension of
Enum
provides a number of useful utilities to get
a List
or Map
of available
Flavor
instances:
// Using Flavor to create an ice cream cone IceCreamCone cone = new IceCreamCone( ); cone.setScoops( 5 ); cone.setFlavor( Flavor.VANILLA ); cone.addTopping( "sprinkles" ); List flavors = Flavor.getEnumList( ); System.out.println( ArrayUtils.toString( flavors.toString( ) ) ); // prints "{ Chocolate, Vanilla, Strawberry }"
Assume that you are writing a system to classify choral singers by
voice type: soprano, alto, tenor, and bass. Your database has a table
dbo.lu_VoicePart
, which contains a part name and a
numeric part code. This voice part is a coded type; it needs both a
name and a code, and a straightforward design is achieved by
extending
ValuedEnum
. ValuedEnum
is an
Enum
that keeps track of a numeric code for each
instance of the enumeration. PartEnum
extends
ValuedEnum
to store static instances of the four
possible voice parts: soprano, alto, tenor, and bass. (See Example 1-11.)
Example 1-11. Extending ValuedEnum to create an enum
import java.util.*; import org.apache.commons.lang.enum.ValuedEnum; public final class PartEnum extends ValuedEnum { public static int SOPRANO_VAL = 1; public static int ALTO_VAL = 2; public static int TENOR_VAL = 3; public static int BASS_VAL = 4; public static PartEnum SOPRANO = new PartsEnum( "Soprano", SOPRANO_VAL ); public static PartEnum ALTO = new PartsEnum( "Alto", ALTO_VAL ); public static PartEnum TENOR = new PartsEnum( "Tenor", TENOR_VAL ); public static PartEnum BASS = new PartsEnum( "Bass", BASS_VAL ); private PartsEnum(String name, int value) { super( name, value ); } public static PartEnum getEnum(String part) { return (PartEnum) getEnum(PartEnum.class, part); } public static PartEnum getEnum(int part) { return (PartEnum) getEnum(PartEnum.class, part); } public static Map getEnumMap( ) { return getEnumMap(PartEnum.class); } public static List getEnumList( ) { return getEnumList(PartEnum.class); } public static Iterator iterator( ) { return iterator(PartEnum.class); } }
This class extends ValuedEnum
and has a private
constructor. This guarantees that the only instances of this
enumeration will be defined in PartEnum
; four
instances of PartEnum
are created as public static
final constants, and a name is assigned to every part. (See Example 1-12.)
Example 1-12. Vocalist bean with a PartEnum
public class Vocalist { private String name; private PartEnum part; public String getName( ) { return name; } public void setName(String name) { this.name = name; } public PartEnum getPart( ) { return part; } public void setPart(PartEnum part) { this.part = part; } }
Checking for every voice part involves a call to
PartEnum.iterator( )
, which obviates the need for
an if
/else if
control statement
to catch every single voice part:
Iterator parts = PartEnum.iterator( ); while( parts.hasNext( ) ) { PartEnum part = (PartEnum) parts.next( ); if( part.equals( vocalist.getPart( ) ) ) { System.out.println( "Vocalist is a " + part.getValue( ) ); } }
This example did not include any reference to a specific voice part,
but, if your code needs to reference a specific voice type, it can
use the static constants in
PartEnum
—PartEnum.SOPRANO
,
PartEnum.ALTO
, PartEnum.TENOR
,
and PartEnum.BASS
. And, lastly, it is impossible
to create a Vocalist
object with an invalid voice
part, as the part property must be one of the four static instances
of PartEnum
defined in a
PartEnum
or null
.
Using Enum types becomes especially important if an object has
multiple classifications. If you were creating an object to track a
person, you could easily end up with two overlapping classifications.
In Example 1-13, both jobStatus
and gender
can be UNKNOWN
, and
the valid options for both jobStatus
and
gender
are defined as public static
int
variables.
Example 1-13. Using public static final constants for category information
public class Person { public static final int STUDENT = -1; public static final int EMPLOYED = 0; public static final int UNEMPLOYED = 1; public static final int RETIRED = 2; public static final int UNKNOWN = 3; public static final int MALE = 0; public static final int FEMALE = 1; private int jobStatus; private int gender; public int getJobStatus( ) { return jobStatus; } public void setJobStatus(int jobStatus) { this.jobStatus = jobStatus; } public int getGender( ) { return gender; } public void setGender(int gender) { this.gender = gender; } }
This class defines two properties—jobStatus
and gender
—and both correspond to codes
stored in the database. This system needs to know; that
MALE
, FEMALE
, and
UNKNOWN
are the valid values for the
gender
property; and STUDENT
,
EMPLOYED
, UNEMPLOYED
,
UNKNOWN
, and RETIRED
all
correspond to the jobStatus
property. This class
does not define which constants are related to which property. Is
UNKNOWN
a gender
or a
jobStatus
? Some systems rename these static
variables to reflect which properties they correspond
to—GENDER_MALE
,
GENDER_FEMALE
, and GENDER_UNKNOWN
would clearly correspond to the
gender
property; and
JOB_STUDENT
, JOB_EMPLOYED
,
JOB_UNEMPLOYED
, JOB_UNKNOWN
,
and JOB_RETIRED
would correspond to the
jobStatus
property. This solution only draws a
conceptual boundary in the mind of the programmer; no logical
separation is achieved, and there is still no way to guarantee that a
bean property will have a valid value. (See Example 1-14.)
Example 1-14. Simplifying with ValueEnum
public class Person { private JobStatusEnum jobStatus; private GenderEnum gender; public JobStatusEnum getJobStatus( ) { return jobStatus; } public void setJobStatus(JobStatusEnum jobStatus) { this.jobStatus = jobStatus; } public GenderEnum getGender( ) { return gender; } public void setGender(GenderEnum gender) { this.gender = gender; } }
The solution to this problem can be solved by extending
ValuedEnum
and getting rid of the confusing
mish-mash of public static constants. If you find yourself frequently
creating static variables to reflect categories or types, enums are
the solution. And, if you haven’t upgraded to Java
1.5, use
Commons Lang
Enum
and ValuedEnum
.
As mentioned previously, Java 1.5 (or Tiger) has a new language feature: enums. If you are using Java 1.5, see the “Java 1.5 in a Nutshell” article from Sun Microsystems at http://java.sun.com/developer/technicalArticles/releases/j2se15/.
Get Jakarta Commons Cookbook now with the O’Reilly learning platform.
O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.