One line at a time

Another Java technology blog from a developer far away from home

Java Enums, that’s the stuff

Share Button

Since Java 1.5, the language includes the enumerated type, everybody knows that. But these guys are offering a very powerful tool here, too often used in the old way because we don’t always realize we’re dealing with a proper Java object.

Where languages like C are just offering an integer representation with enumeration, an Enum in Java is an object with all the characteristics we know about the Java objects.

Behind the scene

So what’s happening when we declare an enum in Java ? Let’s take an enum called AdjustmentType for example, which describes the operation we want to apply on a price:

public enum AdjustmentType {
  INCREASE,
  DECREASE
}

Because of the enum keyword, AdjustmentType becomes implicitly a final class extending the java.lang.Enum abstract class,  the call AdjustmentType.class.getSuperClass() would return the java.lang.Enum class.

Meanwhile, INCREASE and DECREASE  are unique instances of AdjustmentType and will be created automatically when the class is initialized. This step is happening before any static initialization, so you can use the enum instances in your static initialization. Because of this constraint, it means you cannot use any static member in an enum constructor, they don’t exist yet.

Because AdjustmentType is extending java.lang.Enum abstract class, a lots of features come for free ! If you have a look at the source code of Enum, you can see that the class is already implementing the Serializable and Comparable interface. Each instance being unique,  equals and hashCode come for free too. That’s a lot of free useful features !

It’s interesting to see how equals and hashCode have been implemented in OpenJDK. Because INCREASE and DECREASE are Singleton instances, the equals method is just comparing the references between the two objects:

/**
 * Returns true if the specified object is equal to this
 * enum constant.
 *
 * @param other the object to be compared for equality with this object.
 * @return true if the specified object is equal to this
 * enum constant.
 */
 public final boolean equals(Object other) {
   return this==other;
 }

Same thing for hashCode, the implementation relies on the super object of java.lang.Enum, which is java.lang.Object. The hashCode method in Object is a native method, the Java programming language doesn’t specify the implementation technique but for most of the JVM it’s just about converting the internal address of the object into an integer, which works perfectly with Singleton objects.

Note: It’s impossible to extend explicitly java.lang.Enum with your own class, the only way is to declare an enum.

Finally, when an enum is declared, the compiler will automatically add a couple of special static methods like values() and valueOf(String name), the enum doesn’t inherit these methods from java.lang.Enum.

To ensure even more the behaviour described previously (singleton-ness), all the methods of java.lang.Enum are declared final. You can’t override equals, hashCode, ordinal etc.

So, now that we know what’s happening behind the scene,  this is how it would look if we had to write it:

public final class AdjustmentType extends Enum {
  public final static AdjustmentType INCREASE = new AdjustmentType("INCREASE", 0);
  public final static AdjustmentType DECREASE = new AdjustmentType("DECREASE", 1);
  
  protected AdjustmentType(String name, int ordinal) {
    super(name, ordinal);
  }
  
  public static AdjustmentType[] values() {
    return new AdjustmentType[]{AdjustmentType.INCREASE, Adjustment.DECREASE};
  }
  
  public static AdjustmentType valueOf(String name) {
    return AdjustmentType.valueOf(AdjustmentType.class, name);
  }
}

How to get the best out of them ?

A classic use of the enum would be something like this, using switch/case and acting depending the value:

/**
 * Modify a price by applying an adjustment for a given value
 * @param adjustmentType
 * @param price
 * @param modifier
 * @return the price adjusted
 */
public double adjustPrice(AdjustmentType adjustmentType, double price, double modifier) {
  double adjustedPrice;
  switch (adjustmentType) {
   case INCREASE:
     adjustedPrice = price + modifier;
     break;
   case DECREASE:
     adjustedPrice = price - modifier;
     if (adjustedPrice < 0) {
       adjustedPrice = 0;
     }
     break;
   default:
     throw new UnsupportedOperationException("Unhandled value of AdjustmentType");
 }
 return adjustedPrice;
}

It’s doing the job, but we can do something better than this. What if we add a new value in the enum ? We would have to chase every code file where we switch/case on AdjustmentType to handle the new value ?

Remember we said enums in Java are just like the other objects ? So we could create an abstract method in the enum and delegate the logic of the price adjustment to each instance ! These methods are called constant-specific method:

public enum AdjustmentType {
  INCREASE {
    public double adjustPrice(double price, double modifier) {
      return price + modifier;
    }
  },
  DECREASE {
    public double adjustPrice(double price, double modifier) {
      double adjustedPrice = price - modifier;
      return adjustedPrice < 0 ? 0 : adjustedPrice;
    }
  };

  public abstract double adjustPrice(double price, double modifier);
}

With this, no more switch/case chasing, if you have to create a new value of the enum you will be forced by the compiler to implement the method adjustPrice.

That’s just one of many possibilities provided by the enum feature, they can have members, static method, inner class etc. So think twice before doing a switch/case on an enum, and see if the logic behind your code doesn’t belong to the enum itself.

Your enum can’t extend other classes but keep in mind it can implement as many interfaces as you want, becoming an even more powerful Java feature ! Gosh I love these guys.

By the way, in Effective Java, Joshua Bloch shows how to use enum to implement the Singleton pattern in the most efficient and secure way we’ve ever had in Java (multiple instance is impossible, even with reflection, thread safe initialization and memory visibility etc.).

Have fun with enums !

 

Share Button

1 Comment

  1. Great… Thanks for this post..

Leave a Reply to Sobhen Cancel reply

Your email address will not be published.

*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

© 2017 One line at a time

Theme by Anders NorenUp ↑