As Brett McLaughlin described in his article "Getting started with enumerated types" (which I recommend you read as a precursor to this article), you create an enumerated type by creating a named
set of objects using the new enum keyword. You can then treat each named value as an instance of that
class, providing you with compile-time type safety that a named set of integers can't.
Listing 1 creates an enumerated type and uses the type-safe usage as
an argument to a helper method. The values() method of the enumerated
type returns an ordered array of the different values of that type.
Listing 1. Enumerated type example
public class Loop {
enum Size {
Small,
Medium,
Large
}
public static void main(String args[]) {
for (Size s : Size.values()) {
helper(s);
}
}
private static void helper(Size s) {
System.out.println("Size value: " + s);
}
}
|
Constructors, methods, and variables
When you use the enum keyword to create a new enumerated type, you are essentially
creating a subclass of the java.lang.Enum class, where the enumerated type fits into
the generic pattern Class Enum<E extends Enum<E>>, and E
represents the name of your enumerated type. Each value of an enumerated type maps into the
protected Enum(String name, int ordinal) constructor, where the name of each value
is converted to a string, and the ordinal setting represents a one-up value for each setting. In
other words, enum Size {Small, Medium, Large} would map to the constructor calls
shown in Listing 2:
Listing 2. Mapped constructor calls
new Enum<Size>("Small", 0);
new Enum<Size>("Medium", 1);
new Enum<Size>("Large", 2);
|
You don't have to limit the use of constructors to indirect Enum constructor calls.
When you use the enum keyword, you are creating subclasses of Enum. You
can add in your own constructor calls with arguments and everything else for each name defined.
The name declaration is treated as the call to your constructor, and you don't have to add the
new keyword. This approach lets you pass data as argument values to the call, as Listing 3 shows. The argument represents a pricing factor to an enumerated
set of Size objects. The main() method following the enumerated type
definition demonstrates the usage.
Listing 3. Custom constructor example
public class Sample {
enum Size {
Small(0.8),
Medium(1.0),
Large(1.2);
double pricingFactor;
Size(double p) {
pricingFactor = p;
}
}
public static void main(String args[]) {
Size s = Size.Large;
double d = s.pricingFactor;
System.out.println(s + " Size has pricing factor of " + d);
}
}
|
Running the program returns the pricing factor for the given Size. You
can also define a method like getPricingFactor() and make the
pricingFactor field private to treat it more as a
JavaBean-like property. Listing 4 adds a method to the prior example:
Listing 4. Methods example
public class Sample2 {
enum Size {
Small(0.8),
Medium(1.0),
Large(1.2);
private double pricingFactor;
Size(double p) {
pricingFactor = p;
}
public double getPricingFactor() {
return pricingFactor;
}
}
public static void main(String args[]) {
Size s = Size.Large;
double d = s.getPricingFactor();
System.out.println(s + " Size has pricing factor of " + d);
}
}
|
For both cases, the output is:
Large Size has pricing factor of 1.2 |
Because user-defined enumerated types are subclasses of the Enum type, you
inherit all the methods of that class for your types. The complete set of methods is listed
here (E represents the enumerated type itself):
public int compareTo(E e)public boolean equals(Object o)public final Class<E> getDeclaringClass()public int hashCode()public String name()public int ordinal()public String toString()public static <T extends Enum<T>> T valueOf(Class<T> enumType, String name)
Some methods look familiar while others are specific to the Enum class. The
compareTo(), equals(), and hashCode() methods are your
typical Object and Comparable methods, where compareTo()
reports ordering of elements as declared. The name() and ordinal()
methods report back the constructor arguments, and toString() reports back
the name.
The getDeclaringClass() and
valueOf() methods require a little more explanation. The getDeclaringClass() method is like
the getClass() method of Object, but it doesn't necessarily return
the same class. According to the Javadoc for the method:
The value returned by this method may differ from the one returned by the Object.getClass() method for enum constants
with constant-specific class bodies.
I'll explain constant-specific class bodies next. The valueOf() method is static and lets you create an enumerated value from the name for the type.
Constant-specific class bodies
Constant-specific class bodies are a supported feature of the enum keyword;
however, their usage should be severely limited. This concept is getting more into the area of treating
each element of an enumerated type as a subclass. For instance, in the previous examples, the
Size enumerated type had a pricing factor argument and getPricingFactor()
method. Instead of having the constructor argument, Listing 5 shows how to do the same thing with a
constant-specific body. Some extra sizes are added to make the example a little more interesting.
Here, Small has a pricing factor of 0.8, while ExtraLarge and
ExtraExtraLarge have a factor of 1.2. The remaining sizes default to a value of 1.0.
Listing 5. Constant-specific body
public class Sample3 {
enum Size {
Small {
public double getPricingFactor() {
return 0.8;
}
},
Medium,
Large,
ExtraLarge {
public double getPricingFactor() {
return 1.2;
}
},
ExtraExtraLarge {
public double getPricingFactor() {
return 1.2;
}
};
public double getPricingFactor() {
return 1.0;
}
}
public static void main(String args[]) {
for (Size s : Size.values()) {
double d = s.getPricingFactor();
System.out.println(s + " Size has pricing factor of " + d);
}
}
}
|
If you think back to the getDeclaringClass() method I described previously,
you should be able to see why these constant-specific bodies and getClass() can return different
classes while having constant-specific class bodies.
The java.util package includes two classes to help make working with enumerated
types easier: EnumMap and EnumSet. The
EnumMap class offers a special implementation of the java.util.Map interface where the key is an enumerated type. The EnumSet class gives you an implementation of the java.util.Set interface that holds a collection of values of an enumerated
type.
Listing 6 shows the use of an EnumMap class. When you create
the map, you must pass in the class for the enumerated key.
Listing 6. EnumMap example
import java.util.*;
public class EnumMapSample {
enum Size {
Small,
Medium,
Large;
}
public static void main(String args[]) {
Map<Size, Double> map = new EnumMap<Size, Double>(Size.class);
map.put(Size.Small, 0.8);
map.put(Size.Medium, 1.0);
map.put(Size.Large, 1.2);
for (Map.Entry<Size, Double> entry : map.entrySet()) {
helper(entry);
}
}
private static void helper(Map.Entry<Size, Double> entry) {
System.out.println("Map entry: " + entry);
}
}
|
An enumerated set works like a collection of features or a subset of values for all the
elements of an enumerated type. The EnumSet class has the following series of
static methods to get the individual elements out of the enumerated type:
public static <E extends Enum<E>> EnumSet<E> allOf(Class<E> elementType)public static <E extends Enum<E>> EnumSet<E> complementOf(EnumSet<E> s)public static <E extends Enum<E>> EnumSet<E> copyOf(Collection<E> c)public static <E extends Enum<E>> EnumSet<E> noneOf(Class<E> elementType)public static <E extends Enum<E>> EnumSet<E> of(E e)public static <E extends Enum<E>> EnumSet<E> of(E first, E... rest)public static <E extends Enum<E>> EnumSet<E> of(E e1, E e2)public static <E extends Enum<E>> EnumSet<E> of(E e1, E e2, E e3)public static <E extends Enum<E>> EnumSet<E> of(E e1, E e2, E e3, E e4)public static <E extends Enum<E>> EnumSet<E> of(E e1, E e2, E e3, E e4, E e5)public static <E extends Enum<E>> EnumSet<E> range(E from, E to)
Once you've created the EnumSet, you can treat the grouping like any other
Set object.
The basic concept of using enumerated types is simple. You define a
named, closed set of values. When you need one of those values, you
specify it by using its name. The name carries the type of set with
it. Instead of saying 1 = Small, 2 = Medium, 3 = Large
for different sizes and making sure you don't pass in something
where 1 = Monday to a method expecting a Size, you can pass
in Small, Medium, or Large as the Size, because the compiler will make
sure you aren't passing in Monday. That's the simplicity of enumerated
types. These enumerated types are classes themselves, though, so
anything you can do with a class, you can do with an enumerated type.
In addition, enumerated types support having constructors, instance methods, and variables,
among other things. Should you use these aspects with enumerated
types? While it is certainly alright to use the methods and new support
classes, providing constructors and overridden methods just feels
wrong. Does it really make sense to say what price to charge for
each Size in the enumeration? Or, does that make more sense in
a class that has a variable of enum type Size?
Use these features with care, and don't just use them because they are available. Think about the overall design of the system, not just the quick way of doing something.
- Read the complete Taming Tiger series.
- Download J2SE 5.0 from the Sun Developer Network.
- Brett McLaughlin's article "Getting started with enumerated types" (developerWorks, November 2004) explores how to represent constants in a typesafe manner using the Java 5.0 platform. It's a must-read.
- "Enhance looping in Java 5.0 with for/in" (developerWorks, November 2004), also by Brett McLaughlin, helps you discover what this convenient construct offers and when it's most applicable in your projects.
- Read the
Enum,EnumMap, andEnumSetclass Javadocs. - Read about type-safe enums in Sun's J2SE 5.0 documentation.
- To learn more about Java technology, visit the
developerWorks Java zone. You'll find technical documentation, how-to articles,
education, downloads, product information, and more.
- Visit the New to Java technology site for the latest resources to help you get started with Java programming.
- Get involved in the developerWorks community by participating in
developerWorks blogs.
- Browse for books on these and other technical topics.

John Zukowski conducts strategic Java consulting with JZ Ventures, Inc. and is working with SavaJe Technologies to develop a next-generation mobile phone platform. His latest books are The Definitive Guide to Java Swing, Third Edition (Apress, June 2005) and Mastering Java 2, J2SE 1.4 (Sybex, April 2002)
Comments (Undergoing maintenance)





