Use constant types for safer and cleaner code

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

1 2 Page 2
Page 2 of 2

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.
1 2 Page 2
Page 2 of 2