Login | Register   
LinkedIn
Google+
Twitter
RSS Feed
Download our iPhone app
TODAY'S HEADLINES  |   ARTICLE ARCHIVE  |   FORUMS  |   TIP BANK
Browse DevX
Sign up for e-mail newsletters from DevX


advertisement
 

Implementing Enumerated Types in Java

There are many C++ features that Java chose to omit. One is the ability to define enumerated types, such as those declared with the C++ enum keyword.


advertisement
lthough Java syntax borrowed heavily from C++, there are many C++ features that Java chose to omit. At times, the lack of a particular feature makes Java programs cumbersome to implement. One such feature is the ability to define enumerated types, such as those declared with the C++ enum keyword.

An enumerated type can only hold a set of values defined by the programmer. Usually these values behave much like integers, and as such have an implicit ordering. The principal benefit derived from an enumerated type is the ability to restrict the value of a variable to a discrete set, eliminating the need for explicit range checking and leveraging compiler type-checking for error detection.

You might represent a set of discrete volume levels in C++ as follows:



enum Volume { SILENT, SOFT, AUDIBLE, LOUD, ELEVEN };

The most common way to implement this in Java is to define a set of static final integers in an interface, and have any class that requires those values either implement the interface, or explicitly reference the values. For example

public interface Volume { public static final int SILENT = 0; public static final int SOFT = 1; public static final int AUDIBLE = 2; public static final int LOUD = 3; public static final int ELEVEN = 4; }

Here any variable of type integer can represent a volume level. We have caputured the ordering of the values, but have lost both type safety and the limitation of the domain to a discrete programmer-defined set. In the C++ example, if a variable is of type Volume, you know that it can only possess one of the five different values allowable to a Volume type. How do we capture this in Java and also retain type safety?

An often used technique to simulate enumarated types in pure object-oriented programming is to declare a new empty class for each enumerated type, and then create static final members that are instances of the new type which represent the discrete range. We can change our Java implementation to the following:

public final class Volume { public static final Volume SILENT = new Volume(); public static final Volume SOFT = new Volume(); public static final Volume AUDIBLE = new Volume(); public static final Volume LOUD = new Volume(); public static final Volume ELEVEN = new Volume(); private Volume() { // Empty private constructor ensures the only objects of // this type are the enumerated elements declared above. } } public class Stereo { Volume volume = Volume.SILENT; ... }

Now we can guarantee that a Stereo instance can only have a Volume equal to one of the discrete levels we have defined (and null). The problem with this approach is that now we have lost the ordering of the values. We also can no longer manipulate the values as integers, disallowing their use in a switch statement. A natural addition would be to add a member variable to Volume defining the ordering of the levels as follows:

public final class Volume implements Comparable { public static final int SILENT_LEVEL = 0; public static final int SOFT_LEVEL = 1; public static final int AUDIBLE_LEVEL = 2; public static final int LOUD_LEVEL = 3; public static final int ELEVEN_LEVEL = 4; public static final Volume SILENT = new Volume(SILENT_LEVEL); public static final Volume SOFT = new Volume(SOFT_LEVEL); public static final Volume AUDIBLE = new Volume(AUDIBLE_LEVEL); public static final Volume LOUD = new Volume(LOUD_LEVEL); public static final Volume ELEVEN = new Volume(ELEVEN_LEVEL); private static final Volume __VOLUMES[] = { SILENT, SOFT, AUDIBLE, LOUD, ELEVEN }; private int __level; public static final Volume getVolume(int level) { if(level >= SILENT_LEVEL && level <= ELEVEN_LEVEL) return __VOLUMES[level]; return null; } private Volume(int level) { __level = level; } public boolean equals(Object obj) { // Assume proper type was given return (__level == ((Volume)obj).__level); } public int compareTo(Object obj) { // Assume proper type was given int other = ((Volume)obj).__level; if(__level == other) return 0; if(__level < other) return -1; return 1; } public int getLevel() { return __level; } }

This solves the ordering problem and the manipulation of the values as integers. You can determine ordering with equals() and compareTo(). And if you want to use the values in a switch statement, you can fetch a value to test with getLevel() and use the LEVEL constants as the cases. You may be wondering why we added the getVolume() factory method. If we omit the factory method, a small problem arises with respect to serialization. The Volume class has a private constructor and no setter methods so that we may limit the range of values it can represent. This also makes it unserializable. A serializable class must declare Volume variables as transient, and manually store the getLevel() value. On deserialization, the class may recreate its Volume member by using the getVolume() factory method. A benefit of this approach is that it allows you to continue to use the == operator for comparison operations, rather than restrict you to invoking equals(). Another way to deal with serialization to create a readRsolve() method like the following:

private Object readResolve() throws ObjectStreamException { return __VOLUMES[__level]; }

This prevents duplicate constants from being created during deserialization.

It is quite a lot of work just to get the functionality of enumerated types. Simply using integer constants will be sufficient for most programs, but when value restriction and type safety become issues, you will have to resort to some variation of the more complicated solution.



   
Daniel F. Savarese holds a B.S. in astronomy and an M.S. in computer science, both from the University of Maryland, College Park. He is the author of the OROMatcher regular expression library for Java. Reach him here.
Comment and Contribute

 

 

 

 

 


(Maximum characters: 1200). You have 1200 characters left.

 

 

Sitemap
Thanks for your registration, follow us on our social networks to keep up-to-date