Books

Monday, September 09, 2013

Enum in Java.

Let us assume you have the need for some constants in your application; say the three primary colors: Red, Green, Blue. You could create a class specifically for holding the constants thus:
class PrimaryColorsStatic {
        public static final String RED = "red";
        public static final String GREEN = "green";
        public static final String BLUE = "blue";
}

and any of these can be used: PrimaryColorsStatic.RED or PrimaryColorsStatic.GREEN or PrimaryColorsStatic.BLUE

This would work, but it is very limited. Let say you want to hold, apart from the name, the hex code and rgb code of the color as part of the constant? How would this be done with the mechanism above? Whatever way you find to implement this would feel forced as static variables of classes were not designed to help fulfil such use cases.

Enum in Java provides a more robust mechanism for dealing with constant values in your application.

Enum can be formally defined as a special data type that enables for a variable to be a set of predefined constants.

The basic syntax is simple. The primary color example can be represented as an Enum with the following code:
public enum PrimaryColorsEnum {
     RED, GREEN, BLUE
}
[this means that the file would be saved as PrimaryColorsEnum.java]

and an example of accessing it would be:
public main void() {
    PrimaryColorsEnum RedHolder = PrimaryColorsEnum.RED;
    System.out.println(RedHolder);
}

This would print red to the console as a string.

Just knowing the above syntax and when to use it could get you by with Enum, but you have hardly scratched the surface with just that.

To fully appreciate Enums, let us take a step back and see if we can grok the concept in a more detailed level.


Mental Model for Enum.

The mental model I initially had for Enums is similar to the mechanism of static variable members. Meaning if I had this:
class PrimaryColorsStatic {
     public static final String RED = "RED";
     public static final String GREEN = "GREEN";
     public static final String BLUE = "BLUE";
}
it is exactly the same this as
class PrimaryColorsEnum {
     RED, GREEN, BLUE;
}

since PrimaryColorsStatic.RED would print the same thing to the console as PrimaryColorsEnum.RED. This mental model cant be more inaccurate. Thinking Enum variables (or constants) are grouped in the same way as static member variable in a class could not have been a better obstacle in the way of really grokking Enums.

Let us take a look at the example above again:

public main void() {
     PrimaryColorsEnum RedHolder = PrimaryColorsEnum.RED;
     System.out.println(RedHolder);
}

You would notice that RedHolder is being defined to be of data type PrimaryColorsEnum. Which is the Enum and not as a String.

But how come when it is printed to System.out, I see it as a String and not an object or class or enum or String representation of an object?

Well that is because it is cast to string just like most things get cast when printed to the console. This should become clearer by the end of this post when you get to override the toString method. But let us go on in trying to conceptualize a better mental model for representing an Enum.

Enums, Classes, Member Variables, Instance Objetcs

Enumerated types are classes, with each constant in the Enum being a public, final, static member variable in that class. But it does not stop there...The value of the Enum variable (the Enum constant) is then an instance object belonging to the enumerated type class.

It is as simple as that.

So using the PrimaryColorEnum color example. We have RED, GREEN and BLUE as constants. These are actually static member variable that references or points to an object which is an instance of the PrimaryColorsEnum class or better put the PrimaryColorsEnum Enum.

So if you have an Enum, it is a class and the constants references object of that class. These instance objects are the only possible instance objects the Enum can have.

Since each Enum constant is a variable that points to an object, then it means that, just as you would expect, each of these constants would have instance methods and instance variables.

So for example I can have PrimaryColorsEnum.RED.hexcode, PrimaryColorsEnum.GREEN.hexcode and PrimaryColorsEnum.BLUE.hexcode with each pointing to the respective hexcode for the color. This is how you achieve having, apart from the name, the hex code and RGB code as part of the constants.

The new mental model that I had to form is that each of the constants in an Enum is not the literal constant I need. It is an object and it holds values and methods which are the constants I need to keep.

To make this clear let us step back and see more of the syntax:

Since the Enum constant you defined is an instance object of the Enum class then it should be telling that there is more to the syntax of constructing an Enum than previously seen.

To confirm, we see how to add methods (all objects can have methods and you defined methods in classes right?). We add getHexCode, getColorInYoruba and an overidden toString method.

Your PrimaryColorsEnum Enum would then look thus:
public enum PrimaryColorsEnum {
     RED, GREEN, BLUE;

public String getHexCode() {
     if (this == RED) {
        return "#FF0000";
     }

     if (this == GREEN) {
        return "#00FF00";
     }

     if (this == BLUE) {
        return "#0000FF";
    }
}

public String getColorInYoruba() {
     if (this == RED) {
          return "Pupa";
     }

     if (this == GREEN) {
          return "Awo Eweko";
     }

     if (this == BLUE) {
          return "Awo Sanma";
     }

}

   @Overridden
   public String toString () {
 
    String toReturn;

    switch (this) {
        case RED:
             toReturn = "Red color";
        break;

         case GREEN:
             toReturn = "Green color";
         break;

          case BLUE:
             toReturn = "Blue color";
          break;
    }

      return toString;

   }

}


With this, you can get the hex color code of each of the Enum constants:
PrimaryColors.RED.getHexCode();
PrimaryColors.GREEN.getHexCode();
PrimaryColors.BLUE.getHexCode();
also notice the overridden toString Method. With it, when you print any of the Enum constants to the console, you no longer get the constants represented as String but the string literal stated in the overridden method.

Class Constructors in Enum

If Enums are classes, then shouldn't they have constructors too? and if so, how is it used and fit into what I now know?

Enums indeed have constructor functions and to illustrate, we would use the same examples above of adding hex code and color in yoruba to the Enum constant. But instead of implementing this using methods, we would use member variables. We would then have PrimaryColors.RED.hexCode etc.

The code for this would look like:
public enum PrimaryColorEnum {
    RED("#FF0000", "Pupa"),
    GREEN("#00FF00", "Awo Eweko"),
    BLUE("#0000FF", "Awo Sanma");

    public String hexCode;
    public String colorInYoruba;

    private PrimaryColorEnum(String hexcode, String colorinyoruba) {
                this.hexCode = hexcode;
                this.colorInYoruba = colorinyoruba;
    }
}

You would notice that the syntax for defining the Enum constants is now different. With the above syntax, we listed other variables (color in yoruba and hex code) that we want to associate with each Enum constant.

We also defined public variables that would hold these values.

We then defined the Enum Constructor which take these values and uses it when instantiating the instance object of the Enum class that would be referenced by each of the constants.

This shows that Enum just like any class, has constructors which work like you would expect any constructor to work. The only difference with an Enum constructor is that it has to be defined as private.

Also all Enums implicitly extend java.lang.Enum. Java does not support multiple inheritance, this means an Enum cannot extend any other class.

Default Variable Methods of Enums and Enum Constants.

In conclusion, Enums and Enum constant also come with couple of methods which might come in handy. For example, an Enum has values() method which returns all the Enum constants as an Array of the Enum type. While every single Enum constant comes with an ordinal() method which returns the position of that particular constant in the list of Enum constants defined. eg:

PrimaryColorsEnum[] primaryColors = PrimaryColorsEnum.values()

and

PrimaryColorsEnum.RED.ordinal() //Prints 0
PrimaryColorsEnum.GREEN.ordinal() //Prints 1
PrimaryColorsEnum.BLUE.ordinal() //Prints 2

No comments:

Post a Comment