Use constant types for safer and cleaner code

Avoid making typos while encapsulating arbitrary data in legacy systems in an elegant object-oriented way

A couple readers pointed out that one of the constructors for the SmartIntArray has a bug in it. This constructor, which takes only the initial size of the array, calculates the growth size by dividing that initial size by four. If the initial size provided is less than four, the growth size will be calculated as zero, and thus an ArrayOutOfBoundsException will be thrown as soon as the initial size is exceeded. One way to avoid this is to check for a growth size of zero and reset it to one. Another alternative is to define a minimum growth size. I prefer the latter because, the smaller the growth size is, the less efficient the tool becomes.

A few of you also commented that the Vector class has a method called copyInto() which places the vector's contents into the provided array of type Object. This is of no help for primitive types, which is why I wrote the smart arrays. However, the SmartStringArray version is unnecessary, since String is an object, not a primitive, and thus can be passed into the vector's utility method.

On to the new tool

This month's tool is a variation on a theme. I will be expanding on the idea of enumerated constants as covered in Eric Armstrong's July 1997 JavaWorld article, "Create enumerated constants in Java." I strongly recommend reading that article before you immerse yourself in this one, as I will assume that you are familiar with the concepts related to enumerated constants, and I will expand on some of the example code which Eric presented.

As a note before we get started, some of you have commented that, in the tool implementations outlined in my recent articles, I have not taken advantage of the new Java 2 features. This is intentional. In my line of work, I don't see many companies diving into Java 2 yet. Thus, I am presenting tools that are compatible with, and expand on, the 1.1 version of Java.

The concept of constants

In dealing with enumerated constants, I'm going to discuss the enumerated part of the concept at the end of the article. For now, we'll just focus on the constant aspect. Constants are basically variables whose value can't change. In C/C++, the keyword const is used to declare these constant variables. In Java, you use the keyword final. However, the tool introduced here is not simply a primitive variable; it's an actual object instance. The object instances are immutable and unchangeable -- their internal state may not be modified. This is similar to the singleton pattern, where a class can may only have one single instance; in this case, however, a class may only have a limited and predefined set of instances.

The main reasons to use constants are clarity and safety. For example, the following piece of code is not self-explanatory:

    public void setColor( int x ){ ... }
    
    public void someMethod()
    {
        setColor( 5 );
    }

From this code, we can ascertain that a color is being set. But what color does 5 represent? If this code were written by one of those rare programmers who comments on his or her work, we might find the answer at the top of the file. But more likely we'll have to dig around for some old design documents (if they even exist) for an explanation.

A more clear solution is to assign a value of 5 to a variable with a meaningful name. For example:

    public static final int RED = 5;
    public void someMethod()
    {
        setColor( RED );
    }

Now we can tell immediately what is going on with the code. The color is being set to red. This is much cleaner, but is it safer? What if another coder gets confused and declares different values like so:

public static final int RED = 3;
public static final int GREEN = 5;

Now we have two problems. First of all, RED is no longer set to the correct value. Second, the value for red is represented by the variable named GREEN. Perhaps the scariest part is that this code will compile just fine, and the bug may not be detected until the product has shipped.

We can fix this problem by creating a definitive color class:

public class Color 
{
    public static final int RED = 5;
    public static final int GREEN = 7;
}

Then, via documentation and code review, we encourage programmers to use it like so:

    public void someMethod()
    {
        setColor( Color.RED );
    }

I say encourage because the design in that code listing does not allow us to force the coder to comply; the code will still compile even if everything is not quite in order. Thus, while this is a little safer, it is not completely safe. Although programmers should use the Color class, they aren't required to. Programmers could very easily write and compile the following code:

    setColor( 3498910 );

Does the setColor method recognize this large number to be a color? Probably not. So how can we protect ourselves from these rogue programmers? That's where constants types come to the rescue.

We start by redefining the signature of the method:

    public void setColor( Color x ){ ... }

Now programmers can't pass in an arbitrary integer value. They are forced to provide a valid Color object. An example implementation of this might look like this:

    public void someMethod()
    {
        setColor( new Color( "Red" ) );
    }

We are still working with clean, readable code, and we are much closer to achieving absolute safety. But we're not quite there yet. The programmer still has some room to wreak havoc and can arbitrarily create new colors like so:

    public void someMethod()
    {
        setColor( new Color( "Hi, my name is Ted." ) );
    }

We prevent this situation by making the Color class immutable and hiding the instantiation from the programmer. We make each different type of color (red, green, blue) a singleton. This is accomplished by making the constructor private and then exposing public handles to a restricted and well-defined list of instances:

public class Color
{
    private Color(){}
    
    public static final Color RED = new Color();
    public static final Color GREEN = new Color();
    public static final Color BLUE = new Color();
}

In this code we have finally achieved absolute safety. The programmer can't fabricate bogus colors. Only the defined colors may be used; otherwise, the program won't compile. This is how our implementation looks now:

    public void someMethod()
    {
        setColor( Color.RED );
    }

Persistence

Okay, now we've got a clean and safe way to deal with constant types. We can create an object with a color attribute and be certain that the color value will always be valid. But what if we want to store this object in a database or write it to a file? How do we save the color value? We have to map these types to values.

In the JavaWorld article mentioned above, Eric Armstrong used string values. Using strings provides the added bonus of giving you something meaningful to return in the toString() method, which makes debugging output very clear.

Strings, though, can be expensive to store. An integer requires 32 bits to store its value while a string requires 16 bits per character (due to Unicode support). For example, the number 49858712 can be stored in 32 bits, but the string TURQUOISE would require 144 bits. If you are storing thousands of objects with color attributes, this relatively small difference in bits (between 32 and 144 in this case) can add up quickly. So let's use integer values instead. What is the solution to this problem? We'll retain the string values, because they are important for presentation, but we aren't going to store them.

Versions of Java from 1.1 on are able to serialize objects automatically, as long as they implement the Serializable interface. In order to prevent Java from storing extraneous data, you must declare such variables with the transient keyword. So, in order to store the integer values without storing the string representation, we declare the string attribute to be transient. Here's the new class, along with accessors to the integer and string attributes:

public class Color implements java.io.Serializable
{
    private int value;
    private transient String name;
    
    public static final Color RED   = new Color( 0, "Red"   );
    public static final Color BLUE  = new Color( 1, "Blue"  );
    public static final Color GREEN = new Color( 2, "Green" );
    private Color( int value, String name )
    {
        this.value = value;
        this.name = name;
    }
    
    public int getValue()
    {
        return value;
    }
    
    public String toString()
    {
        return name;
    }
}

Now we can efficiently store instances of the constant type Color. But what about restoring them? That's going to be a little tricky. Before we go any further, let's expand this out into a framework that will handle all of the aforementioned pitfalls for us, allowing us to focus on the simple matter of defining types.

The constant type framework

With our firm understanding of constant types, I can now jump into this month's tool. The tool is called Type and it is a simple abstract class. All you have to do is create a very simple subclass and you've got a full-featured constant type library. Here's what our Color class will look like now:

public class Color extends Type
{
    protected Color( int value, String desc )
    {
        super( value, desc );
    }
    public static final Color RED   = new Color( 0, "Red"  );
    public static final Color BLUE  = new Color( 1, "Blue"  );
    public static final Color GREEN = new Color( 2, "Green" );
}

The Color class consists of nothing but a constructor and a few publicly accessible instances. All of the logic discussed to this point will be defined and implemented in the superclass Type; we'll be adding more as we go along. Here's what Type looks like so far:

public class Type implements java.io.Serializable
{
    private int value;
    private transient String name;
    
    protected Type( int value, String name )
    {
        this.value = value;
        this.name = name;
    }
    
    public int getValue()
    {
        return value;
    }
    
    public String toString()
    {
        return name;
    }
}

Back to persistence

With our new framework in hand, we can continue where we left off in the discussion of persistence. Remember, we can save our types by storing their integer values, but now we want to restore them. This is going to require a lookup -- a reverse calculation to locate the object instance based on its value. In order to perform a lookup, we need a way to enumerate all of the possible types.

In Eric's article, he implemented his own enumeration by implementing the constants as nodes in a linked list. I'm going to forego this complexity and use a simple hashtable instead. The key for the hash will be the integer values of the type (wrapped in an Integer object), and the value of the hash will be a reference to the type instance. For example, the GREEN instance of Color would be stored like so:

    hashtable.put( new Integer( GREEN.getValue() ), GREEN );

Of course, we don't want to type this out for each possible type. There could be hundreds of different values, thus creating a typing nightmare and opening the doors to some nasty problems -- you might forget to put one of the values in the hashtable and then not be able to look it up later, for instance. So we'll declare a global hashtable within Type and modify the constructor to store the mapping upon creation:

    private static final Hashtable types = new Hashtable();
    
    protected Type( int value, String desc )
    {
        this.value = value;
        this.desc = desc;
        types.put( new Integer( value ), this );
    }

But this creates a problem. If we have a subclass called Color, which has a type (that is, Green) with a value of 5, and then we create another subclass called Shade, which also has a type (that is Dark) with a value of 5, only one of them will be stored in the hashtable -- the last one to be instantiated.

In order to avoid this, we have to store a handle to the type based on not only its value, but also its class. Let's create a new method to store the type references. We'll use a hashtable of hashtables. The inner hashtable will be a mapping of values to types for each specific subclass (Color, Shade, and so on). The outer hashtable will be a mapping of subclasses to inner tables.

This routine will first attempt to acquire the inner table from the outer table. If it receives a null, the inner table doesn't exist yet. So, we create a new inner table and put it into the outer table. Next, we add the value/type mapping to the inner table and we're done. Here's the code:

    private void storeType( Type type )
    {
        String className = type.getClass().getName();
        
        Hashtable values;
        
        synchronized( types ) // avoid race condition for creating inner table
        {
            values = (Hashtable) types.get( className );
        
            if( values == null )
            {
                values = new Hashtable();
                types.put( className, values );
            }
        }
        
        values.put( new Integer( type.getValue() ), type );
    }

And here's the new version of the constructor:

    protected Type( int value, String desc )
    {
        this.value = value;
        this.desc = desc;
        storeType( this );
    }

Now that we are storing a road map of types and values, we can perform lookups and thus restore an instance based on a value. The lookup requires two things: the target subclass identity and the integer value. Using this information, we can extract the inner table and find the handle to the matching type instance. Here's the code:

    public static Type getByValue( Class classRef, int value )
    {
        Type type = null;
            
        String className = classRef.getName();
        
        Hashtable values = (Hashtable) types.get( className );
        
        if( values != null )
        {
            type = (Type) values.get( new Integer( value ) );
        }
        return( type );
    }

Thus, restoring a value is as simple as this (note that the return value must be casted):

    int value = // read from file, database, etc.
    Color background = (ColorType) Type.findByValue( ColorType.class, value );

Enumerating the types

Thanks to our hashtable-of-hashtables organization, it is incredibly simple to expose the enumeration functionality offered by Eric's implementation. The only caveat is that sorting, which Eric's design offers, is not guaranteed. If you are using Java 2, you can substitute the sorted map for the inner hashtables. But, as I stated in the beginning of this column, I'm only concerned with the 1.1 version of the JDK right now.

The only logic required to enumerate the types is to retrieve the inner table and return its element list. If the inner table doesn't exist, we simply return null. Here's the entire method:

    public static Enumeration elements( Class classRef )
    {
        String className = classRef.getName();
        
        Hashtable values = (Hashtable) types.get( className );
            
        if( values != null )
        {
            return( values.elements() );
        }
        else
        {
            return null;
        }
    }

Finding the largest value

One good feature leads to another. Since we can enumerate the types, we can easily search for the largest type value. This functionality would come in handy if you wanted to extend one type with another. For example:

public class RareColor extends Color
{
    protected RareColor( int value, String desc )
    {
        super( value, desc );
    }
    
    public static final RareColor PERIWINKLE = 
        new RareColor( ( 1 + Type.getMaxValue( Color.class ) ), "Periwinkle"   );
    public static final RareColor TURQUOISE = 
        new RareColor( ( 1 + Type.getMaxValue( RareColor.class ) ), "Turquoise"   );
}

Notice that the first defined RareColor specifies its value as one plus the maximum value of Color (its superclass). But the second declaration defines its value as one plus its own maximum value. This insures that the first instance of the subclass type starts with a unique value and all following instances continue to increment.

The logic for finding the largest value is simply a matter of enumerating all of the types and keeping track of the highest value encountered:

    public static int getMaxValue( Class classRef )
    {
        int max = -1;
        
        Enumeration e = elements( classRef );
                
        while( e.hasMoreElements() )
        {
            Type type = (Type) e.nextElement();
                    
            int tmp = type.getValue();
                    
            if( tmp > max )
            {
                max = tmp;
            }
        }
        
        return( max );
    }

Checking for equality

How do we compare the color values of two different objects for equality? Since these objects are singletons, all we have to do is compare the references. For example:

    if( objectA.color == objectB.color ){ ... }

This code essentially boils down to the following:

    if( Color.RED == Color.RED ){ ... }

But what about subclasses? Rare colors are defined as subclasses of normal colors. Can they be checked for equality? Yes. Observe the following example:

    if( Color.RED == RareColor.RED ){ ... }

Although the color red is being referenced from different classes, both point to the single red type instance.

This all works fine until you start serializing types and passing them around. Singleton objects are not inherent to Java; singletons are a coding trick that we devise as programmers. When Java deserializes one of these type objects, it does not know about the singleton property we are trying to preserve, so Java creates a new instance! Now we have two, maybe more, copies of the red color type floating around. This means that simple reference checking via == is no longer a safe alternative. We must implement the equals() method for Type to insure a consistent and reliable equality check.

The equals() method is implemented as follows. First, we check to see if the supplied object is an instance of Type (or a subclass thereof). If the supplied object is not a Type, then a false value is returned. Note that the instanceof operator will handle null values appropriately and a false value will be returned in those cases as well. Once we are sure that we have a nonnull object reference and the object is indeed a Type, we can check for equivalent references. If the object is indeed being compared to itself, then we return a value of true. If not, we have to compare the class identities and relative value attributes. Here's the code:

    public boolean equals( Object obj )
    {
        if( ! ( obj instanceof Type ) )
        {
            return false;
        }
        
        if( this == obj )
        {
            return true;
        }
        
        if( ( this.getClass() == obj.getClass() )
         && ( this.getValue() == ((Type)obj).getValue() ) )
        { 
            return true;
        }
        return false;
    }

Preventing types with duplicate values

Our tool is now quite powerful and safe to use, but there's one last flaw: There is nothing preventing a programmer from creating two types with the same values. For example:

    public static final Color BLUE   = new Color( 2, "Blue"  );
    public static final Color YELLOW = new Color( 2, "Yellow"  );

To prevent the programmer from declaring types with the same values, we must check for duplicates during initialization. This is accomplished by checking our table of tables to see if another type is already mapped to the value of the type currently being instantiated. Here's how:

    private void checkForDupes( Type type )
    {
        String className = type.getClass().getName();
        
        Hashtable values;
        
        values = (Hashtable) types.get( className );
        
        if( values != null )
        {
            if( values.get( new Integer( type.getValue() ) ) != null )
            {
                System.out.println( "No Dupes Allowed:
                  " + className + "=" + type );
                throw( new RuntimeException() );
            }
        }
    }

I inserted the print statement because there's no easy way to catch and report this error. All the virtual machine is going to tell you is that there was an ExceptionInInitializerError. Java won't even tell you which class or instance threw the exception. My print statement will tell you, as soon as you try to run your program, No Dupes Allowed: Color=Yellow.

Here is the complete source code for the Type tool.

Conclusion

Using constant types in your software will provide you with clean, readable code and safe implementations. They will ensure that programmers maintaining or integrating their own material with your source code will be a lot less likely to break it. As always, comments, criticisms, and design improvement suggestions are always welcome.

Thomas E. Davis is a Sun Certified Java Programmer. He lives in sunny South Florida, but spends every waking second indoors in front of the computer. He has spent the past 2+ years designing and building large-scale multitier distributed applications in Java.
Join the discussion
Be the first to comment on this article. Our Commenting Policies
See more