Enumerations In Java

When I first started learning and working with Java, one of the questions that I was initially faced with is how to use enumerations.

So for example, imagine you are writing a program that needs to deal with a few color codes.

In such a case, we might define several constants in our Java program to make our lives easier. (Note: As Java derives much of its syntax and style from C++, the C++ convention of writing constants in ALL_UPPER_CASE also applies to Java.)

public final int RED_TEXT = 0;
public final int GREEN_TEXT = 1;
public final int BLUE_TEXT = 2;
public final int YELLOW_TEXT = 3;

Using named constants like this will allow us to write clearer code. For example, it they will allow us to avoid cryptic code like this:

void printInColor(int colorCode) {
        switch (colorCode) {
            case 0: // print something in red
            case 1: // print something in green
            [...]
        }
    }

and instead replace it with something like this:

void printInColor(int colorCode) {
        switch (colorCode) {
            case RED_TEXT: // print something in red
            case GREEN_TEXT : // print something in green
            [...]
        }
    }

I tend to think of enumerated types as a way to group related constants together. So for example, instead of defining each color code as a separate constant like we did above, enumerations allow us to group them all together.

The syntax for doing this in Java is straightforward:

enum Color {
    RED_TEXT,
    GREEN_TEXT,
    BLUE_TEXT,
    YELLOW_TEXT
}

But what seemed to be missing in Java is the ability to actually specify ordinal values for the constants, such as we would do like this in C++

enum Color { RED_TEXT=100, GREEN_TEXT, BLUE_TEXT=500, YELLOW_TEXT = 700 }; 

Coming from a background in (old fashion) procedural oriented languages such as Pascal or C++, this type of syntax seems obvious because enumerations in such languages are in essence just integer constants, where the enumerated type that we create (Color) can be cast to an integer and vice verse.

But in the brave new world of purely object oriented languages such as Java, we have to remember that everything is a class!

This applies even to enumerated types, which can be illustrated by doing something weird like this:

enum WeirdEnumeratedType {
    RED_TEXT,
    GREEN_TEXT,
    BLUE_TEXT,
    YELLOW_TEXT;
    
    public static void main(String[] args) {
        System.out.println("In Java, an enerated type is really a class, and as such can have methods, even the program entry point!");
    }
    
}

So once we wrap our head around Java’s concept that “everything is an object”, it starts to make sense how to work with enumerated types in Java.

So the Java equivalent of the C++ enumeration above (where we defined the constants such that RED_TEXT was 100, GREEN_TEXT was implicitly 101, BLUE_TEXT was 500, and YELLOW_TEXT was 700) would be implemented in Java like this:

enum Color {
    RED_TEXT(100),
    GREEN_TEXT(101),
    BLUE_TEXT(500),
    YELLOW_TEXT(700);
    
    private int value; 
    
    private Color(int i) {
        this.value = i;
    }

    int getValue() {
        return this.value;
    }
        
}

Once we get over the initial weirdness of this concept, we can see that Java’s way of doing business actually is both consistent and beautiful. We just have to remember: everything is an object!

In the code sniplet above, you can see that the integer values 100, 101, 500, and 700 are associated with our constants RED_TEXT, GREEN_TEXT, BLUE_TEXT, and YELLOW_TEXT respectively.

We can better understand how this is working if we understand what the compiler is doing internally. In Java, every enumerated constant represents an object whose type is the enumerated type we are creating, which is derived from java.lang.Enum. So the compiler is implicitly creating something like this:

class Color
{
     public static final Color RED_TEXT= new Color(100);
     public static final Color GREEN_TEXT= new Color(101);
     public static final Color BLUE_TEXT= new Color(500);
     public static final Color YELLOW_TEXT = new Color(700);
}

So as we can see, for each constant we are actually getting an instance of our color enum class, and the compiler is calling the constructor method we defined above for each constant, which in turn sets the instance variable value that the getValue() method is returning.

Now that we have this in place, using our newly-created enumerated type is as simple as this:

Color c = Color.BLUE_TEXT;
System.out.println(c + " has an ordinal value of: " + c.getValue());
// output: BLUE_TEXT has an ordinal value of: 500

Consider, for example, this code:

enum Color {
    RED("rojo"),
    GREEN("verde"),
    BLUE("azul"),
    YELLOW("amarillo");
    
    private String spanishName; 
    
    private Color(String s) {
        this.spanishName = s;
    }

    String getSpanishName() {
        return this.spanishName;
    }
        
}

As we can see, the constructor has been redefined to take a String parameter, and instead of associating ordinal values with our constants as we did above, this time we are associating a string value, which in this case is the spanish equivalent of the English color name. Once we’ve created the enumeration above, we can use it in our code like this:

Color c = Color.RED;
System.out.println(c + " is expressed in Spanish as " + c.getSpanishName());
//output: RED is expressed in Spanish as rojo

Conclusion

When learning and working with a new programming language our temptation sometimes is to try to adopt our existing style of thinking to the new language. In many cases it is OK to do this, because in many aspects different programming languages are similar.

But we also have to remember that each programming language (or at least, each class of programming language) in essence represents a different way to think about the problem we are trying to solve.

Keeping this principle in mind helps us to write better and more logical code within the domain of the language at hand, because it let’s us work with instead of struggling against the paradigm on which the language is based.

And in Java, the basic paradigm we must adopt is that everything is an object!