Enums in Java and how to use them

An enum type is a type whose fields consist of a fixed set of constants. And you can use only constant names to perform some logic. Common examples include compass directions (values of NORTH, SOUTH, EAST, and WEST). Because they are constants, the names of an enum type’s fields are recommended to be in uppercase.

Let’s take for example type of the protocol that you can use. Without enums you could use just Java constants like this:

public static String PROTOCOL_GRPC = "GRPC";
public static String PROTOCOL_XMLRRPC = "XMLRPC";
public static String PROTOCOL_JSONRPC = "JSONRPC";

For some cases it could be a good solution. But it has one obvious caveat, all your constants are of type String and there is not way to distinguish enum string from any other string variable. Sometime you would require to add more information to your constant. If we would follow previous example logic than we have to add another constant which is not OK.

public static String PROTOCOL_GRPC = "grpc";
public static int PROTOCOL_GRPC_TIMEOUT_MS = 1000;

By using enum you could group all logically similar constants in one place. You should use enum types any time you need to represent a fixed set of constants. That includes natural enum types where you know all possible values at compile time, for example: days of week, the choices on a menu, command line flags, and so on.

public enum RpcProtocol {GRPC, XMLRPC, JSONRPC; };
// Each enum constant has a String name
System.out.println(RpcProtocol.GRPC.name());

Now all your constants are of type RpcProtocol and you can get their names as String or acquire constant by it’s name.

But the most interesting thing is that enum is actually a class (RpcProtocols) and each constant behaves as unique instance of this class (RpcProtocols.GRPC). Because RpcProtocols is a class we could add methods to it and this methods would be available for all constants. Also we could add some properties to enum class. The main thing with enum classes is that we are not able to instantiate it instances with new keyword. We are only able to call package-protected constructor while declaring enum constants. Consider this as some kind of syntax sugar.

Now if we want to have some state associated with enum we could create some private property to hold this state, some getter and add property initialization in protected constructor.

public enum RpcProtocol {
    // Now we are passing some strange
    // parameters while defining our enum
    GRPC(500), XMLRPC(5000), JSONRPC(1000);

    // This is how you store enum property
    private final int timeout;

    // This constructor is used 
    // while you construct your enum
    RpcProtocol(int timeout) {
        this.timeout = timeout;
    }

    // It is also good idea to return property
    // to let other code use it
    public int getTimeout() {
        return this.timeout;
    }
}

Java programming language enum types are much more powerful than their counterparts in some older languages like C/C++. The compiler automatically adds some special methods when it creates an enum. For example, they have a static values method that returns an array containing all of the values of the enum in the order they are declared. This method is commonly used in combination with the for-each construct to iterate over the values of an enum type. For example, this code from the Planet class example below iterates over all the planets in the solar system.

for (Planet p : Planet.values()) {
    System.out.printf("Your weight on %s is %f%n",p, p.surfaceWeight(mass));
}

All enums implicitly extend java.lang.Enum. Since Java does not support multiple inheritance, an enum cannot extend anything else. In the following example, Planet is an enum type that represents the planets in the solar system. They are defined with constant mass and radius properties.

In addition to its properties and constructor, Planet has methods that allow you to retrieve the surface gravity and weight of an object on each planet. Here is a sample program that takes your weight on earth (in any unit) and calculates and prints your weight on all of the planets (in the same unit):

public enum Planet {
    MERCURY (3.303e+23, 2.4397e6),
    VENUS   (4.869e+24, 6.0518e6),
    EARTH   (5.976e+24, 6.37814e6),
    MARS    (6.421e+23, 3.3972e6),
    JUPITER (1.9e+27,   7.1492e7),
    SATURN  (5.688e+26, 6.0268e7),
    URANUS  (8.686e+25, 2.5559e7),
    NEPTUNE (1.024e+26, 2.4746e7);
    private final double mass;   // in kilograms
    private final double radius; // in meters
    Planet(double mass, double radius) {
        this.mass = mass;
        this.radius = radius;
    }
    private double mass()   { return mass; }
    private double radius() { return radius; }
    // universal gravitational constant  (m3 kg-1 s-2)
    public static final double G = 6.67300E-11;
    double surfaceGravity() {
        return G * mass / (radius * radius);
    }
    double surfaceWeight(double otherMass) {
        return otherMass * surfaceGravity();
    }
    public static void main(String[] args) {
        if (args.length != 1) {
            System.err.println("Usage:  java Planet <earth_weight>");
            System.exit(-1);
        }
        double earthWeight = Double.parseDouble(args[0]);
        double mass = earthWeight/EARTH.surfaceGravity();
        for (Planet p : Planet.values())
           System.out.printf("Your weight on %s is %f%n",
                             p, p.surfaceWeight(mass));
    }
}

If you run Planet.class from the command line with an argument of 175, you get this output:

$ java Planet 175
Your weight on MERCURY is 66.107583
Your weight on VENUS is 158.374842
Your weight on EARTH is 175.000000
Your weight on MARS is 66.279007
Your weight on JUPITER is 442.847567
Your weight on SATURN is 186.552719
Your weight on URANUS is 158.397260
Your weight on NEPTUNE is 199.207413

That is all folks. Now you know basic things about Java enums.