Avoid Final Strings For Unique Types

This idiom is closely related to EnumeratedTypesInJava, but it addresses a simpler problem and proposes a simpler solution...

Here is a common (poor) idiom that you may encounter in a lot of Java code:

	class Direction {
	  public final static String NORTH = "North";
	  public final static String SOUTH = "South";
	  public final static String EAST = "East";
	  public final static String WEST = "West";

public void setOrientation(String aHeading) { if (aHeading.equals(NORTH) { ... etc .. } }

public static void main(String args[]) { Direction dir = new Direction(); dir.setOrientation(Direction.NORTH); }

The goal is to provide unique (specified) types for parameters. It seeks to emulate C's #define or enum constructs. The problem is that any String can be passed to setOrientation. The only way to check for valid parameters is to do so at runtime. The aHeading.equals() is expensive, but you could optimize it by checking to see if the exact reference to NORTH is passed in (aHeading == NORTH).

In JDK 1.1, by using inner classes, there is a much more elegant workaround. Consider:

	public class Direction {

// Define base type for constants // private static class Heading { };

public final static Heading NORTH = new Heading(); public final static Heading SOUTH = new Heading(); public final static Heading EAST = new Heading(); public final static Heading WEST = new Heading();

public void setOrientation(Heading aHeading) { if (aHeading == NORTH || aHeading == SOUTH) { System.out.println("Head for the pole!"); } } public static void main (String argv[]) { Direction dir = new Direction(); dir.setOrientation(Direction.NORTH); dir.setOrientation(Direction.EAST); dir.setOrientation(Direction.SOUTH); } }

Now you have compile time checking on the parameter aHeading. Using your own base type. is not much more expensive than using Strings. The one problem, though, is that the finals carry no printable representation of themselves. This can be remedied if you want to add a little extra code:

	private static class Heading {
	  private String dir;
	  Heading(String aDirection) {
		dir = aDirection;
	  }
	  public String toString() {
		return dir;
	  }
	}
	public final static Heading NORTH = new Heading("NORTH");
	public final static Heading SOUTH = new Heading("SOUTH");

public void setOrientation(Heading aHeading) { System.out.println("You are now Facing " + aHeading); if (aHeading == NORTH || aHeading == SOUTH) { System.out.println("Head for the pole!"); } }

You could do all of this without using inner classes (the JVM doesn't care), but I think that it is a cleaner, clearer organization of code to keep the typed constant in the same class it is contextually bound to.

-- ToddCoram

My approach would be to avoid exposing the direction values. Instead of having a generic setOrientation method with a required parameter, create four discrete parameterless methods, such as setNorth. This also avoids the need for having the if statement within the setOrientation method. Don't force the coder to go look for a specific value to path and then evaluate the parameter at runtime! This simplifies coding all the way around.

	public class Direction {

public void setNorth() { System.out.println("Head for the pole!"); } public void setSouth() { System.out.println("Head for the pole!"); } public void setWest() { System.out.println("Go west young man!"); } public void setEast() { System.out.println("Could not think of anything cute to say here"); }

} public static void main (String argv[]) { Direction dir = new Direction(); dir.setNorth(); dir.setEast(); dir.setSouth(); } }

But this has the following disadvantages: The Direction class can provide internal storage, if necessary (nothing in the original example specified a need), and the format is not exposed. The direction could be save as a text string, numeric values, boolean values, or a set of child classes (i.e., DirectionNorth implements Direction, etc.). As for reading the direction, one needs to look at the reason for "reading". Is it to determine the direction? In that case create a set of methods like isNorth, leading to a line of code like

  if(dir.isNorth()){}.  

If the purpose is to compare Direction classes, then create a method like Equals, leading to a line of code like

  if(dir1.Equals(dir2)) {}.  

If the purpose is for textual display, then create a method like ShowName, leading to a line of code like

  theDirection = dir.ShowName();


See SillyJavaEnumerationRefactorings.
Be careful of serialisation if you use this. -- DaveHarris

What is the problem with serialisation of this code --SeshKumar

The de-serialization process will create new instances even if the constructor is private. Object.equals won't have the correct semantics anymore so you will have to override it. We just compare the string. -- NeilSwingler

That's what readResolve() is for. You add an index to the class, you mark dir as transient, you put the instances into an array, and you use readResolve() to replace the deserialized object with the one in the array. If you make this class Cloneable, you write the clone() method to return this. No string comparisons are necessary. It isn't appropriate for this class, but you can easily make it Serializable too. --EricJablow

The strings are nice for debugging. If you wanted to be more efficient, you could also create a Symbol class which takes a string to its constructor, intern()s it, implements equals() with ==, and has a nice toString(). Though maybe in practice it's OK to rely on the VM's constant string sharing for the identity? I think I read that all equal strings that come from (different) class file constant pools will share identity in recent JDKs. Won't help if you're pulling the strings out of XML parse trees or something, though! -- LukeGorrie


See also JavaSerialization CategoryJava
EditText of this page (last edited August 24, 2005)
FindPage by browsing or searching

This page mirrored in JavaIdioms as of April 29, 2006