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:
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