Skip to main content

If you don't have an IBM ID and password, register here.

By clicking Submit, you agree to the developerWorks terms of use.

The first time you sign into developerWorks, a profile is created for you. This profile includes the first name, last name, and display name you identified when you registered with developerWorks. Select information in your developerWorks profile is displayed to the public, but you may edit the information at any time. Your first name, last name (unless you choose to hide them), and display name will accompany the content that you post.

All information submitted is secure.

The first time you sign in to developerWorks, a profile is created for you, so you need to choose a display name. Your display name accompanies the content you post on developerworks.

Please choose a display name between 3-31 characters. Your display name must be unique in the developerWorks community and should not be your email address for privacy reasons.

By clicking Submit, you agree to the developerWorks terms of use.

All information submitted is secure.

Getting started with enumerated types

Represent constants in a typesafe manner using Java 5.0

Brett McLaughlin (brett@newInstance.com), Author/Editor, O'Reilly Media, Inc.
Author photo
Brett McLaughlin has worked in computers since the Logo days (remember the little triangle?) with such companies as Nextel Communications and Lutris Technologies. In recent years he has become one of the most well-known authors and programmers in the Java and XML communities. His most recent book, Java 1.5 Tiger: A Developer's Notebook, is the first book available on the newest version of Java, and his classic Java and XML remains one of the definitive works on using XML technologies in Java.

Summary:  One of the great new features in Tiger is the enumeration construct, a new type that allows you to represent specific pieces of data as constants, all in a type-safe manner. Tiger expert and frequent developerWorks contributor Brett McLaughlin explains what an enumeration is, how you can use it in your programs, and why it will allow you to dump all that old public static final code.

Date:  09 Nov 2004
Level:  Introductory

Comments:  

You already know that the two fundamental building blocks of Java code are classes and interfaces. Now Tiger has introduced one more: the enumeration. Usually referred to simply as an enum, this new type allows you to represent specific data points that accept only certain sets of pre-defined values at assignment time.

Of course, well-practiced programmers already know you can achieve this functionality with static constants, as shown in Listing 1:


Listing 1. Public static final constants
public class OldGrade {

  public static final int A = 1;
  public static final int B = 2;
  public static final int C = 3;
  public static final int D = 4;
  public static final int F = 5;
  public static final int INCOMPLETE = 6;
}

Note: I'd like to thank O'Reilly Media, Inc., which has permitted me to use the code sample from the "Enumerations" chapter of my book Java 1.5 Tiger: A Developer's Notebook for this article (see Resources).

You can then set up classes to take in constants like OldGrade.B, but when doing so keep in mind that such constants are Java ints, which means the method will accept any int, even if it doesn't correspond to a specific grade defined in OldGrade. Therefore, you'll need to check for upper and lower bounds, and probably include an IllegalArgumentException if an invalid value appears. Also, if another grade is eventually added (for example, OldGrade.WITHDREW_PASSING), you'll have to change the upper bound on all your code to allow for this new value.

In other words, while using classes with integer constants like this might be a passable solution, it's not a very efficient one. Fortunately, enumerations offer a better way.


Defining an enum

Listing 2 uses an enumeration to provide similar functionality to Listing 1:


Listing 2. Simple enumerated type
package com.oreilly.tiger.ch03;

public enum Grade {
  A, B, C, D, F, INCOMPLETE
};

Here I've used the new keyword enum, given the enum a name, and specified the allowed values. Grade then becomes an enumerated type, which you can use in a manner shown in Listing 3:


Listing 3. Using an enumerated type
package com.oreilly.tiger.ch03;

public class Student {

  private String firstName;
  private String lastName;
  private Grade grade;

  public Student(String firstName, String lastName) {
    this.firstName = firstName;
    this.lastName = lastName;
  }

  public void setFirstName(String firstName) {
    this.firstName = firstName;
  }

  public String getFirstName() {
    return firstName;
  }

  public void setLastName(String lastName) {
    this.lastName = lastName;
  }

  public String getLastName() {
    return lastName;
  }

  public String getFullName() {
    return new StringBuffer(firstName)
           .append(" ")
           .append(lastName)
           .toString();
  }

  public void assignGrade(Grade grade) {
    this.grade = grade;
  }

  public Grade getGrade() {
    return grade;
  }
}

By creating a new enumeration (grade) of the previously defined type, you can then use it like any other member variable. Of course, the enumeration can be assigned only one of the enumerated values (for example, A, C, or INCOMPLETE). Also, notice how there is no error checking code or boundary considerations in assignGrade().


Working with enumerated values

The examples you've seen so far have been rather simple, but enumerated types offer much more. Enumerated values, which you can iterate over and use in switch statements, among other things, are very valuable.

Iterating over enums

Let's begin with an example that shows how to run through the values of any enumerated type. This technique, shown in Listing 4, is handy for debugging, quick printing tasks, and loading enums into a collection (which I'll talk about shortly):


Listing 4. Iterating over enumerated values
public void listGradeValues(PrintStream out) throws IOException {
  for (Grade g : Grade.values()) {
    out.println("Allowed value: '" + g + "'");
  }
}

Running this snippet of code will give you the output shown in Listing 5:


Listing 5. Output of iteration
Allowed Value: 'A'
Allowed Value: 'B'
Allowed Value: 'C'
Allowed Value: 'D'
Allowed Value: 'F'
Allowed Value: 'INCOMPLETE'

There's a lot going on here. First, I'm using Tiger's new for/in loop (also called foreach or enhanced for). Additionally, you can see that the values() method returns an array of individual Grade instances, each with one of the enumerated type's values. In other words, the return type of values() is Grade[].

Switching on enums

Being able to run through the values of an enum is nice, but even more important is the ability to make decisions based on an enum's values. You could certainly write a bunch of if (grade.equals(Grade.A))-type statements, but that's a waste of time. Tiger has conveniently added enum support to the good old switch statement, so it's easy-to-use and fits right in with what you already know. Listing 6 shows you how to pull this off:


Listing 6. Switching on enums
public void testSwitchStatement(PrintStream out) throws IOException {
  StringBuffer outputText = new StringBuffer(student1.getFullName());

  switch (student1.getGrade()) {
    case A: 
      outputText.append(" excelled with a grade of A");
      break;   
    case B: // fall through to C
    case C: 
      outputText.append(" passed with a grade of ")
                .append(student1.getGrade().toString());
      break;
    case D: // fall through to F
    case F:
      outputText.append(" failed with a grade of ")
                .append(student1.getGrade().toString());
      break;
    case INCOMPLETE:
      outputText.append(" did not complete the class.");
      break;
  }

  out.println(outputText.toString());
}

Here the enumerated value is passed into the switch statement (remember, getGrade() returns an instance of Grade) and each case clause deals with a specific value. That value is supplied without the enum prefix, which means that instead of writing case Grade.A you need to write case A. If you don't do this the compiler won't accept the prefixed value.

You should now understand the basic syntax involved in using switch statements, but there are still a few things you need to know.

Planning ahead with switch

You can use the default statement with enums and switches, just as you would expect. Listing 7 illustrates this usage:


Listing 7. Adding a default block
public void testSwitchStatement(PrintStream out) throws IOException {
  StringBuffer outputText = new StringBuffer(student1.getFullName());

  switch (student1.getGrade()) {
    case A: 
      outputText.append(" excelled with a grade of A");
      break;   
    case B: // fall through to C
    case C: 
      outputText.append(" passed with a grade of ")
                .append(student1.getGrade().toString());
      break;
    case D: // fall through to F
    case F:
      outputText.append(" failed with a grade of ")
                .append(student1.getGrade().toString());
      break;
    case INCOMPLETE:
      outputText.append(" did not complete the class.");
      break;
    default:
      outputText.append(" has a grade of ")
                .append(student1.getGrade().toString());
      break;
  }

  out.println(outputText.toString());
}

Consider the code above and realize that any enumerated value not specifically processed by a case statement is instead processed by the default statement. This is a technique you should always employ. Here's why: Suppose that the Grade enum was changed by another programmer in your group (who of course forgot to tell you about it) to the version shown in Listing 8:


Listing 8. Adding values to the Grade enum
package com.oreilly.tiger.ch03;

public enum Grade {
  A, B, C, D, F, INCOMPLETE, 
  WITHDREW_PASSING, WITHDREW_FAILING
};

Now, if you used this new version of Grade with the code in Listing 6, these two new values would be ignored. Even worse, you wouldn't even see an error! In these cases, having some sort of general purpose default statement is very important. Listing 7 may not handle these values gracefully, but it will give you some indication that values have snuck in, and that you need to address them. Once you've done that you'll have an application that continues to run, doesn't ignore values, and that even even instructs you to take later action. Now that's good coding.


Enums and collections

Those of you familiar with the public static final approach to coding have probably already moved on to using enumerated values as keys to maps. For the rest of you who don't know what this means, take a look at Listing 9, which is an example of common error messages that can pop up when working with Ant build files:


Listing 9. Ant status codes
package com.oreilly.tiger.ch03;

public enum AntStatus {
  INITIALIZING,
  COMPILING,
  COPYING,
  JARRING,
  ZIPPING,
  DONE,
  ERROR
}

Having some human-readable error message assigned to each status code would allow you to look up the appropriate error message and echo it back out to the console when Ant supplies one of the codes. This is a great use-case for a Map, in which each key of the Map is one of these enumerated values, and each value is the error message for that key. Listing 10 illustrates how this works:


Listing 10. Maps of enums
public void testEnumMap(PrintStream out) throws IOException {
  // Create a map with the key and a String message
  EnumMap<AntStatus, String> antMessages =
    new EnumMap<AntStatus, String>(AntStatus.class);

  // Initialize the map
  antMessages.put(AntStatus.INITIALIZING, "Initializing Ant...");
  antMessages.put(AntStatus.COMPILING,    "Compiling Java classes...");
  antMessages.put(AntStatus.COPYING,      "Copying files...");
  antMessages.put(AntStatus.JARRING,      "JARring up files...");
  antMessages.put(AntStatus.ZIPPING,      "ZIPping up files...");
  antMessages.put(AntStatus.DONE,         "Build complete.");
  antMessages.put(AntStatus.ERROR,        "Error occurred.");

  // Iterate and print messages
  for (AntStatus status : AntStatus.values() ) {
    out.println("For status " + status + ", message is: " +
                antMessages.get(status));
  }
}

This code uses both generics (see Resources) and the new EnumMap construct to create a new map. Also, an enumerated type is supplied via its Class object, along with the type of the Map's values (in this case, simple strings). The output of this method is shown in Listing 11:


Listing 11. Output of Listing 10
[echo] Running AntStatusTester...
[java] For status INITIALIZING, message is: Initializing Ant...
[java] For status COMPILING, message is: Compiling Java classes...
[java] For status COPYING, message is: Copying files...
[java] For status JARRING, message is: JARring up files...
[java] For status ZIPPING, message is: ZIPping up files...
[java] For status DONE, message is: Build complete.
[java] For status ERROR, message is: Error occurred.

Enum's Class object?

You may have noticed that the sample code in Listing 10 actually implies Tiger treats enums as classes, which is evidenced by the AntStatus Class object being not only available, but also in use. This is true. Under the hood, Tiger views enums as a special class type. For more information on the implementation details of enums, see Chapter 3 of Java 5.0 Tiger: A Developer's Notebook (see Resources).


Going further

Enums can also be used in conjunction with sets, and much like the new EnumMap construct, Tiger supplies a new Set implementation EnumSet that allows you to work with bitwise operators. Additionally, you can add methods to your enums, use them to implement interfaces, and define what are called value-specific class bodies, where specific code is attached to a particular value of an enum. These features are beyond the scope of this article, but they're well documented elsewhere (see Resources).


Use them, but don't abuse them

One of the dangers of learning a new version of any language is the tendency to go crazy with new syntactical structures. Do that and suddenly your code is 80 percent generics, annotations, and enumerations. So use enumerations only where they make sense. Where, then, do they make sense? As a general rule, anywhere constants are in use, such as in places you are currently using the switch code to switch constants. If it's a single value (for example, an upper bound on a shoe size, or the maximum number of monkeys that can fit in a barrel), leave the constant as it is. But if you're defining a set of values, and any one of those values can be used for a certain data type, enumerations should be a perfect fit.


Resources

About the author

Author photo

Brett McLaughlin has worked in computers since the Logo days (remember the little triangle?) with such companies as Nextel Communications and Lutris Technologies. In recent years he has become one of the most well-known authors and programmers in the Java and XML communities. His most recent book, Java 1.5 Tiger: A Developer's Notebook, is the first book available on the newest version of Java, and his classic Java and XML remains one of the definitive works on using XML technologies in Java.

Report abuse help

Report abuse

Thank you. This entry has been flagged for moderator attention.


Report abuse help

Report abuse

Report abuse submission failed. Please try again later.


developerWorks: Sign in

If you don't have an IBM ID and password, register here.


Forgot your IBM ID?


Forgot your password?
Change your password


By clicking Submit, you agree to the developerWorks terms of use.

 


The first time you sign into developerWorks, a profile is created for you. This profile includes the first name, last name, and display name you identified when you registered with developerWorks. Select information in your developerWorks profile is displayed to the public, but you may edit the information at any time. Your first name, last name (unless you choose to hide them), and display name will accompany the content that you post.

Choose your display name

The first time you sign in to developerWorks, a profile is created for you, so you need to choose a display name. Your display name accompanies the content you post on developerWorks.

Please choose a display name between 3-31 characters. Your display name must be unique in the developerWorks community and should not be your email address for privacy reasons.

(Must be between 3 – 31 characters.)


By clicking Submit, you agree to the developerWorks terms of use.

 


Rate this article

Comments

Help: Update or add to My dW interests

What's this?

This little timesaver lets you update your My developerWorks profile with just one click! The general subject of this content (AIX and UNIX, Information Management, Lotus, Rational, Tivoli, WebSphere, Java, Linux, Open source, SOA and Web services, Web development, or XML) will be added to the interests section of your profile, if it's not there already. You only need to be logged in to My developerWorks.

And what's the point of adding your interests to your profile? That's how you find other users with the same interests as yours, and see what they're reading and contributing to the community. Your interests also help us recommend relevant developerWorks content to you.

View your My developerWorks profile

Return from help

Help: Remove from My dW interests

What's this?

Removing this interest does not alter your profile, but rather removes this piece of content from a list of all content for which you've indicated interest. In a future enhancement to My developerWorks, you'll be able to see a record of that content.

View your My developerWorks profile

Return from help

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=Java technology
ArticleID=31910
ArticleTitle=Getting started with enumerated types
publish-date=11092004
author1-email=brett@newInstance.com
author1-email-cc=dwxed@us.ibm.com

Tags

Help
Use the search field to find all types of content in My developerWorks with that tag.

Use the slider bar to see more or fewer tags.

For articles in technology zones (such as Java technology, Linux, Open source, XML), Popular tags shows the top tags for all technology zones. For articles in product zones (such as Info Mgmt, Rational, WebSphere), Popular tags shows the top tags for just that product zone.

For articles in technology zones (such as Java technology, Linux, Open source, XML), My tags shows your tags for all technology zones. For articles in product zones (such as Info Mgmt, Rational, WebSphere), My tags shows your tags for just that product zone.

Use the search field to find all types of content in My developerWorks with that tag. Popular tags shows the top tags for this particular content zone (for example, Java technology, Linux, WebSphere). My tags shows your tags for this particular content zone (for example, Java technology, Linux, WebSphere).