Saturday, October 20, 2012

A Counter solution to Java 5 Enum in Java 1.4

Java 5 has a very good feature: enum. The enum keyword can be used to create C++ like enums. The enum created in Java 5 are a particular type of class whose instances are only those objects which are members of that enum. Each member of the enum is an instance of that enum class. The super class of all enum objects is java.lang.Enum and enums can not be extended. Java 5 also supports methods to be contained in an enum.
For example:
public enum MeasurementType {
Length, Mass, Time;
}

The following code works perfectly fine for the above enum and the output is shown below:
System.out.println(MeasurementType.Length instanceof MeasurementType);
System.out.println(MeasurementType.Length);
Output:
true
Length

Now, there are many situations where we need to use enums and the above stated features of enums but we can not use Java 5 features because of some requirements of the project (like many projects are created to run on JDK 1.3/1.4 because client environment does not support JDK 5 or some other reason like that). If such a case arises then many developers feel upset and devise their own method to tackle the problem at hand. Sometimes, there are situations where we have written a library using Java 5 features like enums but we need to use that library in a legacy project and port the whole code to Java 2. If we are using annotations, then we need to discard all the annotations and enums. Rest of the Java 5 features are binary compatible with earlier JVMs and we can easily port the library. But here comes the hardest thing: Discarding Enums. Annotations are still least understood and least used feature of Jaa 5 and mostly used by Framework Junkies in special type of libraries like ORM etc. But enums are very popular and there is not possible to change each and every line of code that is using enum class.
I here present a solution that can be used to replace an enum class with a Java 2 class and you need not change the code using that enum class anywhere.
Look at the following code:

public abstract class MeasurementType {
private MeasurementType() {
//constructor made private so that any classes inheriting from this class may not be created.
}

public static final MeasurementType Length = new MeasurementType() {
public String toString() {
return "Length";
}
};

public static final MeasurementType Mass = new MeasurementType() {
public String toString() {
return "Mass";
}
};

public static final MeasurementType Time = new MeasurementType() {
public String toString() {
return "Time";
}
};
}

Now the code shown above that used an enum MeasurementType works perfectly fine with class MeasurementType.
System.out.println(MeasurementType.Length instanceof MeasurementType);
System.out.println(MeasurementType.Length);
Output:
true
Length

If your enums had any methods inside them, they can be included in the abstract class created to replace the enum class. The java file containing the enum class is to be replaced by the file containing abstract class and obviously the package will be same so that all the classes using the enum may be compiled without any changes.
This way you can replace all your Java 5 enums and compile all your libraries for Java 2.

The pattern shown above is a well known solution to create enum like structures in Java. The class is kept abstract and constructor is kept private so that no classes can be created inherited from the enumeration class and no new members can be added in enum which are not included originally by the creator of the lilbrary.

Happy Porting....

Sandeep Beniwal

-------
Hi,
Thanks for reminding that I am missing values() and valueOf() methods of Java 5 Enum in my Enum class written for Java 2. Actually I was not using these methods in my little library that I needed to port to Java 2.
We can add these two methods to our MeasurementType example, as follows:

private static final MeasurementType[] values = new MeasurementType[]{Length, Mass, Time};
public static final MeasurementType[] values() {
return values;
}

public static final MeasurementType valueOf(String value) {
if("Length".equals(value)) {
return Length;
}
else if("Mass".equals(value)) {
return Mass;
}
else if("Time".equals(value)) {
return Time;
}
return null;
}

2 comments: