Create enumerated constants in Java

The weaknesses of Java's static finals are defined here and a template is provided for creating typesafe constants

1 2 Page 2
Page 2 of 2

public final class Color { private String id; public final int ord; private Color prev; private Color next;

private static int upperBound = 0; private static Color first = null; private static Color last = null; private Color(String anID) { this.id = anID; this.ord = upperBound++; if (first == null) first = this; if (last != null) { this.prev = last; last.next = this; } last = this; } public String toString() {return this.id; } public static int size() { return upperBound; } public static Color first() { return first; } public static Color last() { return last; } public Color prev() { return this.prev; } public Color next() { return this.next; }

public static final Color RED = new Color("Red"); public static final Color GREEN = new Color("Green"); public static final Color BLUE = new Color("Blue"); }

Whenever a new constant object is created, it is linked to the last constant object, and the last constant object is linked to it. The static methods first() and last() return the end points of the list, while the methods prev() and next() move from object to object within the list. The beauty of all this is that once the class is set up, you only have to modify the last few lines to create or remove constants from the class; their order is determined by the order in which you create them.

Enumerations

The last modification is to extend the class so that you can use a standard Enumeration interface, like this:

  import java.util.*;
  ...
  Enumeration e = Color.elements();
  while (e.hasMoreElements()) {
    Color c = (Color) e.nextElement();
    ...
  }

The Enumeration interface is defined in java.util, so that package needs to be imported. The interface says that the object has two methods. One method, hasMoreElements(), returns a boolean. The other, nextElement(), returns an object of type Object -- which forces you to cast it to an object of type Color using (Color) in order to assign the value to a Color variable.

The really cool and largely unsung merit of interfaces is that you can use an interface as a data type! The elements method in Color will return an object, and that object will belong to a special class. But the user won't even care what that class is! The only relevant feature of the class will be that it implements the Enumeration interface. The method invoking the class can then write:

  Enumeration e = Color.elements();

regardless of what the actual class is that elements() returns.

Here are the modifications necessary to return a class that implements the Enumeration interface:

import java.util.*;

public final class Color { private String id; public final int ord; private Color prev; private Color next;

private static int upperBound = 0; private static Color first = null; private static Color last = null; private Color(String anID) { this.id = anID; this.ord = upperBound++; if (first == null) first = this; if (last != null) { this.prev = last; last.next = this; } last = this; } public static Enumeration elements() { return new Enumeration() { private Color curr = first; public boolean hasMoreElements() { return curr != null; } public Object nextElement() { Color c = curr; curr = curr.next(); return c; } }; } public String toString() {return this.id; } public static int size() { return upperBound; } public static Color first() { return first; } public static Color last() { return last; } public Color prev() { return this.prev; } public Color next() { return this.next; }

public static final Color RED = new Color("Red"); public static final Color GREEN = new Color("Green"); public static final Color BLUE = new Color("Blue"); }

This code uses an "anonymous (inner) class." Both anonymous classes and inner classes are new in Java 1.1. An inner class is a class that is defined inside of another class -- so it cannot be used outside of that class. An anonymous class is one without a name -- a "one-shot" class that is used to instantiate a single object. An anonymous class can be used either to extend an existing class or to implement an interface. In this case, it implements the Enumeration interface. The "new" operator creates a new object using the definition in the curly braces that follow it.

When an inner class references a local variable from the containing class, that variable must be declared as final. That restriction is imposed by Java so that the virtual machine and compiler do not have to worry about a variable that could be simulatenously modified in two different classes. In this case, the only such variable accessed is first, which just happens to be final.

Conclusion

In this article, you have learned how to create typesafe constants that are ordered, enumerable, printable, usable as an index, and usable in a loop. Below is the complete template for constants that meet these criteria. It's a fairly hefty chunk of code, but it is a useful template: If you do a search and replace on "Color" you can create any class of constants you like. Then you need only modify the last few lines to define the constants you need. Copy this template and modify the parts that are highlighted:

package enumTest; import java.util.*;

public final class Color { private String id; public final int ord; private Color prev; private Color next;

private static int upperBound = 0; private static Color first = null; private static Color last = null; private Color(String anID) { this.id = anID; this.ord = upperBound++; if (first == null) first = this; if (last != null) { this.prev = last; last.next = this; } last = this; } public static Enumeration elements() { return new Enumeration() { private Color curr = first; public boolean hasMoreElements() { return curr != null; } public Object nextElement() { Color c = curr; curr = curr.next(); return c; } }; } public String toString() {return this.id; } public static int size() { return upperBound; } public static Color first() { return first; } public static Color last() { return last; } public Color prev() { return this.prev; } public Color next() { return this.next; }

public static final

Color

RED

= new

Color

("

Red

"); public static final

Color

GREEN

= new

Color

("

Green

"); public static final

Color

BLUE

= new

Color

("

Blue

"); }

Finally, here is a test file that shows what happens when you enumerate over the constants defined in the Color class:

package enumTest; import java.util.*;

public class EnumTest { //Main method static public void main(String[] args) { Enumeration e = Color.elements(); while (e.hasMoreElements()) { Color c = (Color) e.nextElement(); System.out.println(c + ": " + c.ord); } } }

Note: If compilation produces the error, "class Color not found," make sure your classpath variable includes "..". With the Color class in the enumTest package, the compiler sees "Color" as "enumTest.Color." The installation instructions for JDK 1.1 say to include "." in the classpath, but under Windows, at least, that causes the compiler to look for Color.java under the current directory (enumTest) instead of in that directory.

Eric Armstrong has been programming and writing professionally since before there were personal computers. His production experience includes AI programs, system libraries, real-time programs, and business applications in a variety of languages. He is currently writing a book on a soon-to-be-released Java IDE.

Learn more about this topic

1 2 Page 2
Page 2 of 2