Constants, I do declare

Readers describe how they employ constants

In "Constants" (JavaWorld, March 30, 2001), I wrote, "I'd be interested to hear how others declare their constants and why. I can post your responses in a future Q&A." Well, here are some reader responses.

It turns out that using an interface to declare constants may not be the best practice after all. In fact, using interfaces in this way may be downright nasty. Why? Interfaces should only be used to define types; any other use is an abuse. Using an interface causes internal details -- such as constants -- to leak into the class' public API. Once something becomes part of the public API, you can never get rid of it. Thanks to Josh Bloch for smacking me upside the head and setting me straight.

Josh pointed me to his excellent article, "Substitutes for Missing C Constructs," (java.sun.com), which describes a design pattern for creating design patterns in Java. Such a pattern works very nicely for declaring XML tags. Be sure to look out for Josh's upcoming book, Effective Java Programming Language Guide (Addison-Wesley, June 2001).

Originally, I advocated using an interface. Consider the following XML markup:.

 

I would have created the following interface:

public interface RecipeMarkup {
   public final static String RECIPES_TAG    = "Recipes";
   public final static String RECIPE_TAG     = "Recipe";
   public final static String INGREDIENT_TAG = "Ingredient";
}

By following the pattern, I can create the following class instead:

public class Markup {
   private final String tag;
// make protected so we can override if new tags are added
   protected Markup(String tag) { this.tag = tag; }
public String toString()  { return tag; }
   // might also add a compare(String) method to simplify comparison
   // such as when we want to handle SAX events
   
   public static final Markup RECIPES =
      new Markup("Recipes");
   public static final Markup RECIPE =
      new Markup("Recipe");
   public static final Markup INGREDIENT =
      new Ingredient("Ingredient");
}

By adding a compare(String) method, we can simplify tag comparison. For example, if you use SAX, you have to do a string comparison to figure out what tag you're dealing with in the event. This pattern is great for enumerations; the XML markup should be an enum. However, there are some other ways to define pure run-of-the-mill constants.

Jay Baker sent me a good suggestion. I'll let him say it in his own words:

I prefer the use of an abstract class with public static final constants for most implementations. This is just as easy to implement. Whether an interface or a class, one must still define the interface or class and put the constants in there. The best reason to use this method is that it more closely matches the design concept. Using an abstract class is an implementation of the "uses" association that is really what you want with constants. Implementing an interface implies something that is not really there in the case of constants.

Josh Bloch made a similar suggestion:

I'd still recommend against the use of "constant interfaces." Interfaces should be used to define types; any other use constitutes abuse. If the constants are subservient to some other class or interface, put them into it. If not, make a "utility class" containing the constants:

public class RecipeMarkup {
    private RecipeMarkup() { } // Prevents instantiation
    public static final String RECIPES_TAG    = "Recipes";
    public static final String RECIPE_TAG     = "Recipe";
    public static final String INGREDIENT_TAG = "Ingredient";
}

Note that I've done three things to your interface: First, I turned it into a class. Second, I added a private constructor to suppress the default constructor, thus guaranteeing noninstantiability. Third, I made all the constants final. Your constants weren't final, so a malicious or confused client could change them.

One other thing worth mentioning: you said that you wanted the possibility of changing the strings. Under these circumstances, you'd be better off with access methods:

public class RecipeMarkup {
    private RecipeMarkup() { } // Prevents instantiation
    private static final String RECIPES_TAG    = "Recipes";
    private static final String RECIPE_TAG     = "Recipe";
    private static final String INGREDIENT_TAG = "Ingredient";
    public static recipesTag     { return RECIPES_TAG; }
    public static recipeTag      { return RECIPE_TAG; }
    public static ingredientTag  { return INGREDIENT_TAG; }
}

The reason: If you expose the strings, they get compiled into clients. If you then change them, it has no effect on the clients unless you recompile them. If, on the other hand, you use access methods, the new values immediately propagate to all of the clients.

Geert Mergan also sent me an interesting example of a "dynamic" constant:

public class Constant
{
    static private Properties RESOURCE;
    static public void setResource(Properties props)
    {
        RESOURCE = props;
    }
    private String _constantName;
    private String _defaultValue;
    private String cache;
    public Constant(String constantName, String defaultValue)
    {
        _constantName = constantName;
        _defaultValue = defaultValue;
    }
    public String toString()
    {
        if (cache == null)
        {
            cache = RESOURCE.getProperty(_constantName, _defaultValue);
        }
        return cache;
    }
}

And the usage:

package test;
public class X
{
    final static private Constant SIZE = new Constant("test.SIZE", "0");
    public static void main(String[] args)
    {
        // skip initialization with Properties for sake of simplicity
        System.out.println("size is: " + SIZE);
    }
}

Geert adds: "You can have lots of optimizations. Just to name a few: the constant doesn't have a default value, use System properties instead of a Properties object, support nonstring constants (int, java.util.Date, boolean, ... )"

Thanks to all who responded!

Tony Sintes is a principal consultant at BroadVision. Tony, a Sun-certified Java 1.1 programmer and Java 2 developer, has worked with Java since 1997.

Learn more about this topic

Join the discussion
Be the first to comment on this article. Our Commenting Policies