Skip to main content

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

The first time you sign into developerWorks, a profile is created for you. Select information in your profile (name, country/region, and company) is displayed to the public and will accompany any content you post. You may update your IBM account at any time.

All information submitted is secure.

  • Close [x]

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.

  • Close [x]

Taming Tiger: Beyond the basics of enumerated types

Work effectively with typesafe constants in Java 5.0

John Zukowski (jaz@zukowski.net), President, JZ Ventures, Inc.
Author photo
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)

Summary:  In November 2004, Brett McLaughlin got you started using enumerated types with the Java™ 5.0 platform. In this month's Taming Tiger, columnist John Zukowski explains how to work with enumerated classes and their predefined methods and shows how to add constructors, override methods, and have instance variables.

View more content in this series

Date:  19 Apr 2005
Level:  Introductory
Also available in:   Japanese

Activity:  14950 views
Comments:  

The basics

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


Predefined methods

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.


EnumMap and EnumSet

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.


Summary

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.


Resources

About the author

Author photo

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)

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


Need an IBM ID?
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. Select information in your profile (name, country/region, and company) is displayed to the public and will accompany any content you post. You may update your IBM account at any time.

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

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=Java technology
ArticleID=75715
ArticleTitle=Taming Tiger: Beyond the basics of enumerated types
publish-date=04192005
author1-email=jaz@zukowski.net
author1-email-cc=jaloi@us.ibm.com