Java - - By Valdio Veliu

Fundamentals of Java Enum Types

Whatever code you’re working on, chances are you will sooner or later need a list of constants, like Monday, Tuesday, etc. for all weekdays or January, Februrary, etc. for all months. Java has you covered with enum types, usually only called enums, which make it exceedingly easy to define just that. In this article I will teach you everything you need to know to become a proficient user of Java enums.

Enum Fundamentals

In its simplest form a Java enum is just a fixed number of constants that the developer defines while writing the code. (It could, for example, define all SitePoint channels.) Much like a class, an enum defines a type that can be used just about anywhere where classes and interfaces could be used, for example for fields, variables, and parameters.

Enums can actually be much more than mere constants, having their own attributes and methods, but I will come to that later. Seeing them as constants is a good way to get to know them.

Defining an Enum

An enum is defined similarly to how a class would be defined but uses the enum instead of the class keyword. The constant values are listed in the enum’s body (meaning within the curly braces). While not necessary, it is common to write the constants in all uppercase letters to make it easier to recognize them.

Now that we have a description for enums, it’s time for an example. Here is what an enum looks like that lists some of the SitePoint channels:

public enum SitePointChannel {
    JAVA,
    MOBILE,
    WEB,
    PHP,
    WORDPRESS,
    JAVASCRIPT,
    DESIGN
}

The elements inside the SitePointChannel enum are called enumeration constants.

Assigning an Enum

Once an enum is defined, you can assign its values to variables. Here is an example:

private SitePointChannel channel;

The channel field is of type SitePointChannel, therefore the only values that it can be assigned are those defined by this enumeration. As a local variable that could look as follows:

SitePointChannel channel = SitePointChannel.JAVA;

Using an Enum

Enumerations have a variety of features in Java. One of their core capabilities is that they can be used in identity comparisons and switch statements.

Identity Comparisons With ==

Enumeration constants can be compared for identity by using the relational operator ==. Here is an example of an if() condition, which in this case is always false.

SitePointChannel channel = SitePointChannel.JAVA;
if (channel == SitePointChannel.MOBILE) {
    System.out.println("...");
}

Switch Statements

Another very widely used feature of enumerations is the ability to control switch statements. Here is an example that prints the content of a given channel:

SitePointChannel channel = ... // specify an enumeration constant
switch (channel) {
    case JAVA:
        System.out.println("Java, web and desktop technologies");
        break;
    case MOBILE:
        System.out.println("Mobile technologies");
        break;
    case PHP:
        // as usual for switch, once a match was found, the execution
        // "falls through" to the next branch until it hits a break
    case WEB:
    case JAVASCRIPT:
    case WORDPRESS:
        System.out.println("Web technologies");
        break;
    default:
        throw new IllegalArgumentException(
                "Unknown channel '" + channel + "'.")
        break;
}

In switch statements, enumeration constants are used without their enumeration type name. This is due to the fact the enum type is implicitly specified in the switch expression. It is good practice to list all enum constants (even if some of them don’t do anything) and add a default branch, usually with an exception (in case a new constant gets added and someone misses the switch statement and doesn’t update it.)

Enums for a list of sonstants - traffic lights, maybe?

Enums as a Class

With the basics of Java enums covered we can go beyond the interpretation of enums as a fixed number of constants. Enums are, in fact, much more like classes!

They can have fields and methods as well as implement interfaces and I will explain all of that in a minute. Even the enumeration constants are not that special – they are just public, static, and final members of their enum type. The only reason we don’t have to put public static final in is that the compiler fills it in for us.

The major differences towards regular classes is that they can not extend other classes (see below why) and can not be created with the new keyword (to keep the number of constants fixed).

Extending java.lang.Enum

All enums implicitly extend java.lang.Enum. In Java, a class can only extend one parent and therefore an enum cannot extend any other class (but implement interfaces – see below).

Extending Enum means that every enum has a few methods that make it more usable:

  • static values()
  • static valueOf(String)
  • name()
  • ordinal()
  • compareTo(Enum)

Enum‘s values() Method

The values() method returns an array of enum-type variables containing all of the enumeration constants. Here is an example:

SitePointChannel[] channels = SitePointChannel.values();
for (SitePointChannel channel : channels) {
    System.out.println(channel + " Channel");
}

And the output:

JAVA Channel
MOBILE Channel
WEB Channel
PHP Channel
WORDPRESS Channel
JAVASCRIPT Channel
DESIGN Channel

As you can see, the values() methods provides a nice solution to loop over all constants of an enum.

Enum‘s valueOf(String) Method

The valueOf(String) method returns an enumeration constant whose value corresponds to the string passed to it or throws an IllegalArgumentException if no constant with the specified name was found. Careful, the strings passed to valueOf are case sensitive!

// returns SitePointChannel.JAVA enum
SitePointChannel.valueOf("JAVA");

// throws IllegalArgumentException
SitePointChannel.valueOf("java");

As pointed out previously, all enumerations automatically inherit java.lang.Enum. This class defines several methods, available for all the enumerations.

The name() method

This method returns the name of this enum constant, exactly as declared in its enum declaration. Here is an example:

SitePointChannel channel = SitePointChannel.JAVA;
System.out.println(channel.name());

The output of this example is the String JAVA.

The ordinal() method

This method is used to obtain an enumeration constant’s position in the list of constants. This is called the ordinal value. Here is an example:

SitePointChannel channel = SitePointChannel.JAVA;
System.out.println(channel.ordinal());

The output of this example is “0” since JAVA is the first constant of the Sitepoint enum and the initial constant is assigned an ordinal of zero. Hence, the following outputs “3”:

SitePointChannel channel = SitePointChannel.PHP;
System.out.println(channel.ordinal());

The compareTo(Enum) method

This method is used to compare the ordinal value of two constants of the same enumeration. This method returns a negative integer, zero, or a positive integer based on the ordinal positions of the two instances that are being compared.

This is how this works. Let’s take, for example, as a reference enum instance SitePointChannel.PHP.

SitePointChannel channel  = SitePointChannel.PHP;

Surely if we compare the channel instance with SitePointChannel.PHP, the compareTo() method will return the value 0.

// returns 0
channel.compareTo(SitePointChannel.PHP);

Since the reference enum instance is in the fourth position in the enum class and has both elements prior and subsequent to its position, it can return both positive and negative outputs with this method.

For example, if it is compared with SitePointChannel.JAVA, the output of the compareTo() method will be positive since SitePointChannel.JAVA is located before the reference instance:

// actually returns 3
channel.compareTo(SitePointChannel.JAVA);

If the reference enum instance is compared with instances that come after it in the enum class, it will return a negative value. For example, comparing it to SitePointChannel.JAVASCRIPT will return the value -2.

// returns -2
channel.compareTo(SitePointChannel.JAVASCRIPT);

Enums with Fields

Java enumeration constants can have fields, which must be given a value at creation time. Keep in mind that as with instances of a regular class, each enumeration constant has its own fields. In order to define values, the enclosing type must have a constructor that accepts parameters. As I mentioned earlier, each enumeration constant is an object of its enumeration type, so a constructor is called for each of the enumeration constants. Fields and constructors (as well as methods) must be defined below the list of constants.

Let’s go through an example for this problem. Suppose we have the SitePointChannel enumeration with the list of channels and we need to add to each channel the number of published articles. I will declare a field for it:

public enum SitePointChannel {

    JAVA,
    MOBILE,
    WEB,
    PHP,
    WORDPRESS,
    JAVASCRIPT,
    DESIGN;

    private int numberOfArticles;
}

So far, so good, but the field is never assigned a value. To do that we also need a constructor:

// DOES NOT COMPILE
public enum SitePointChannel {

    // [...] list of constants as before

    private int numberOfArticles;

    Sitepoint(int numberOfArticles) {
        this.numberOfArticles = numberOfArticles;
    }
}

Unfortunately this does not compile because the constants are initiated with the no-args constructor that no longer exists. To fix this, we call the new constructor:

public enum SitePointChannel {

    JAVA(1344),
    MOBILE(2444),
    WEB(4311),
    PHP(5311),
    WORDPRESS(3221),
    JAVASCRIPT(3865),
    DESIGN(3492);

    // [...] field and constructor as before

}

The same logic is true for multiple instance variables and different data types (int, double, String, …).

Enums with Methods

Besides the basic method available to all enums you can add custom methods to add additional functionalities.

The following example demonstrates the usefulness of enums with methods. Previously in this article, I mentioned that the valueOf() method is case sensitive. The following solution will give an alternative solution to the valueOf() method without the limitations of case sensitivity, by creating our own method.

public enum SitePointChannel {

    JAVA,
    MOBILE,
    WEB,
    PHP,
    WORDPRESS,
    JAVASCRIPT,
    DESING;

    public static SitePointChannel valueOfIgnoreCase(String channelName) {
        channelName = channelName.toUpperCase();
        return valueOf(channelName);
    }

}

To make sure this solution works, run the following code snippet.

if (SitePointChannel.valueOfIgnoreCase("jaVa") == SitePointChannel.JAVA){
    System.out.println("Ignore case enum");
}

As you can see from the example, the valueOfIgnoreCase() function takes the string “jaVa” and returns the JAVA enum. This solution works for any combination of uppercase and lowercase characters of a string representing a SitePointChannel enum instance.

Implementing interfaces

Given the ability to have fields and methods it makes sense that enums can also implement interfaces. This works exactly like it would in regular classes so there is little to be said about it.

Here’s a simple example:

public interface Printable {

    void print();

}

public enum SitePointChannel implements Printable {

    // [...] constants, field and constructor as before

    @Override
    public void print() {
        System.out.println("Channel " + name()
                + " has " + numberOfArticles + " articles.");
    }

}

Summary

That’s all on the fundamentals of Java enums. We saw that they can describe a fixed number of constants and how Enum‘s methods values(), valueOf(String), name(), ordinal(), and compareTo(Enum) can be used. But we also discovered that they are full-blown classes, which allow adding fields, creating methods, and implementing interfaces.

Sponsors