http://madbean.com/2004/mb2004-3/
https://discursive.atlassian.net/wiki/display/CJCOOK/Creating+an+Enum
Problem
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 coneIceCreamCone 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/.