Typesafe Enum


Definition

Define a class representing a single element of the enumerated type and provide no public constructor. An enumeration is a new type since Java 5 (jdk1.5). Before jdk1.4, you can create a similar type that is much better and type safe. This is so-called Typesafe Enum pattern.

Where to use & benefits

Example for jdk1.5

//simple enum type
 public enum Coin {
     PENNY,
     NICKEL,
     DIME,
     QUARTER;
 }
to use:

switch(coin) {
    case PENNY: return 1;
    case NICKEL: return 5;
    case DIME: return 10;
    case QUARTER: return 25;
    default: return 0;
}


//enum type with a method 
 public enum Coin {
     PENNY,
     NICKEL,
     DIME,
     QUARTER;

     int value(){
        switch(this) {
         case PENNY: return 1;
         case NICKEL: return 5;
         case DIME: return 10;
         case QUARTER: return 25;
         default: return 0;
       }
    }
 }
to use:

int v = coin.value();

//enum type with abstract type
 public enum Coin {
     PENNY { 
         int value(){ return 1;} 
     },
     NICKEL { 
         int value(){ return 5;}
     },
     DIME { 
         int value() { return 10;}
     },
     QUARTER { 
         int value() {return 25;}
     };

     abstract int value();
 }

//enum type with constructor
 public enum Coin {
     PENNY(1),
     NICKEL(5),
     DIME(10),
     QUARTER(25);

     private int coinValue;

     int value() {return coinValue;}

     Coin(int value){
       coinValue = value;
     }
 }

 //enum combining with visitor design pattern
 public abstract class CoinVisitor {
     void visitPenny(Coin c){}
     void visitNickel(Coin c){}
     void visitDime(Coin c){}
     void visitQuarter(Coin c){}
 }

 public enum Coin {
     PENNY {
       void accept(CoinVisitor cv) {cv.visitPenny(this);}
     },
     NICKEL {
       void accept(CoinVisitor cv) {cv.visitNickel(this);}
     },
     DIME {
       void accept(CoinVisitor cv) {cv.visitDime(this);}
     },
     QUARTER {
       void accept(CoinVisitor cv) {cv.visitQuarter(this);}
     };
     abstract void accept(CoinVisitor cv);
 }

Example for type transfer for jdk1.5 below

If you are a C or C++ or C# programmer, you may be familiar with the code below.

typedef enum {
   WHITE,
   BLACK,
   YELLOW
}color_t;
typedef enum {
   CAMRY,
   HONDA,
   FORD
}car_t;

Once defined, an enumeration is used very much like an integer type. By default enumerator values are assigned increasing from 0, so WHITE == 0, BLACK == 1 and YELLOW == 2, and so does for enum car_t. You may see the following code:

color_t myColor = FORD;

The above code has a problem because the color and car have been mixed. Such type is not safe.

So let's see how Java does it. Use Java equivalent design, for example, define a playing card class, you may try to do it in the following way,

public class PlayingCard {
    public static final int SUIT_CLUBS =0;
    public static final int SUIT_DIAMONDS =1;
    public static final int SUIT_HEARTS =2;
    public static final int SUIT_SPADES =3;
    ...
}

The above code seems OK for switch statement, but is problematic and not type safe either.

Here we use typesafe enum pattern, use a class to represent constants:

//The typesafe enum pattern
public class Suit {
    private final String name;
    
    public static final Suit CLUBS =new Suit("clubs");
    public static final Suit DIAMONDS =new Suit("diamonds");
    public static final Suit HEARTS =new Suit("hearts");
    public static final Suit SPADES =new Suit("spades");    
    
    private Suit(String name){
        this.name =name;
    }
    public String toString(){
        return name;
    }

}

Note that, the constructor is private, it prevents subclasses. The constants are static so they are easily accessible. Such design makes a compile-time type safe.

You may use it somewhere like C's enum type in the following way:

private static final Suit[] CARD_SUIT = {
    CLUBS,
    DIAMONDS,
    HEARTS,
    SPADES 
};
...
if (suit ==Suit.CLUBS){
    ...
}else if (suit ==Suit.DIAMONDS){
    ...
}else if (suit ==Suit.HEARTS){
    ...
}else if (suit ==Suit.SPADES){
    ...
}else{
   throw new NullPointerException("Null Suit");
   //suit ==null
}

Later, you want to expand Suit class, like the following, you don't need to recompile the client class.

//Ordinal-based typesafe enum
public class Suit implements Comparable {
    private final String name;
    
    public static final Suit CLUBS =new Suit("clubs");
    public static final Suit DIAMONDS =new Suit("diamonds");
    public static final Suit HEARTS =new Suit("hearts");
    public static final Suit SPADES =new Suit("spades");
    
    //Ordinal of next suit to be created
    private static int nextOrdinal =0;
   
   //Assign an ordinal to this suit
    private final int ordinal =nextOrdinal++;
    
    private Suit(String name){
        this.name =name;
    }
    public String toString(){
        return name;
    }
    public int compareTo(Object o){
       return ordinal -((Suit)o).ordinal;
    }
}

Reference: book "Effective Java Programming".

Return to top