Home | FAQ | Contact me

Java 5 Enumerations, part 3: Nuts and Bolts

A good example...

import java.util.HashMap;
import java.util.Map;

import static java.util.Objects.isNull;

import org.junit.Test;

public class ExtensionTypeTest
{
  enum ExtensionType
  {
    race     ( "us-core-race" ),
    ethnicity( "us-core-ethnicity" ),
    religion ( "us-core-religion" ),
    unknown  ( "unknown" );

    private String URL;

    ExtensionType( String URL ) { this.URL = URL; }

    public String getURL() { return URL; }

    private static final Map< String, ExtensionType > VALUES = new HashMap<>();

    static
    {
      for( ExtensionType type : ExtensionType.values() )
        VALUES.put( type.getURL(), type );
    }

    public static ExtensionType fromUrl( final String URL )
    {
      ExtensionType type = VALUES.get( URL );
      return( isNull( type ) ) ? ExtensionType.unknown : type;
    }
  }

  @Test
  public void test()
  {
    ExtensionType type = ExtensionType.fromUrl( "us-core-ethnicity" );
    System.out.println( "  " + type.name() );
  }

  @Test
  public void testScrewed() throws NullPointerException
  {
    ExtensionType type = ExtensionType.fromUrl( "us-core-gender" );
    System.out.println( "  " + type.name() );
  }
}

Output is

  ethnicity
  unknown

Another example...

Consider this class definition for the rest of this discussion. You'll refer back to it (sometimes by line number).

package com.etretatlogiciels.examples.enums;

public enum Foo
{
  CONSTANT1 ( 1, "One" ),
  CONSTANT2 ( 2, "Two" );

  private int    ordinal;
  private String spelling;

  private Foo( int ordinalvalue, String spelling )
  {
      this.ordinal  = ordinalvalue;
      this.spelling = spelling;
  }

  public int    getOrdinal()  { return this.ordinal; }
  public String getSpelling() { return this.spelling; }

  public static void main( String[] args )
  {
      Foo f;

      ...
  }
}

Here is a list of individual bits of functionality you might wish to associate with enums, but were afraid to ask how. Foo will be the enum used to demonstrate the concepts in this write-up.

  1. Terminology
  2. Render an enum from a string matching its constant field
  3. Get an enum ordinal value
  4. Find by field
  5. Get an enum's constant field as a string
  6. Course through all values in a loop
  7. Determine if a value is contained without looping
  8. Appendix: the complete enum Foo class

Terminology

enum, a Java keyword beginning with Java 1.5.

enum constants are Java objects. They are sometimes called fields.

Render an enum from a string matching its constant field

Add this line to the main() method, to line 24.

    Foo f = Foo.valueOf( "CONSTANT1" );

If you do the following, on line 24, it will fail with the message shown here.

    Foo f = Foo.valueOf( "1" );

    Exception in thread "main" java.lang.IllegalArgumentException: \
        No enum const class com.etretatlogiciels.examples.enums.Foo.1
	    at java.lang.Enum.valueOf(Enum.java:196)
	    at com.etretatlogiciels.examples.enums.Foo.valueOf(Foo.java:1)
	    at com.etretatlogiciels.examples.enums.Foo.main(Foo.java:24)

...or, if you do the following, it will also fail as noted.

    Foo f = Foo.valueOf( "One" );

    Exception in thread "main" java.lang.IllegalArgumentException: \
        No enum const class com.etretatlogiciels.examples.enums.Foo.One
	    at java.lang.Enum.valueOf(Enum.java:196)
	    at com.etretatlogiciels.examples.enums.Foo.valueOf(Foo.java:1)
	    at com.etretatlogiciels.examples.enums.Foo.main(Foo.java:24)

Get an enum ordinal value

To get the ordinal value, or relative position within the enum, a method internal to Enum, on which Java enums are based, is used. Add the following to the main() method to see how this works. Examine the implementation of this method.

        System.out.println( f.getOrdinal() );

There is also, as you'll see below in the finished example, the possibility of f.ordinal itself. What comes back from f.ordinal isn't actually identical to what comes back from getOrdinal(): ordinality is implemented here for illustration, but it really comes for free in Java enums. Imagine that we wrote a different example and put these ordinal numbers in a jumbled order. f.getOrdinal() would be very different from f.ordinal.

Find by field

We've added an instance variable, spelling, to Foo. This is completely arbitrary. It could be called anything and be of almost any type.

Here's how to use spelling, in this case, "One" or "Two", to instantiate a Foo. Add this method to the enum code. Notice that it's static. This is because you won't want to have to use an instance of Foo in order to get (the same or) another instance. Also, add a couple of more lines to the main() method to demonstrate it.

    public static Foo findBySpelling( String spelling )
    {
        for( Foo foo : Foo.values() )
        {
            if( spelling.equals( foo.getSpelling() ) )
                return foo;
        }

        return null;
    }

    public static void main( String[] args )
    {
        Foo f;

//        f = Foo.valueOf( "One" );    (don't crash!)
        f = Foo.valueOf( "CONSTANT1" );
        System.out.println( f );

        f = Foo.findBySpelling( "Two" );
        System.out.println( f );
    }

The output from the change above is:

    CONSTANT1
    CONSTANT2

Get an enum's constant field as a string

You want to render the constant enumeration field as a string. Add this to the code. Note the name() method belonging to the underlying implementation of Enum on which enums are based. Don't confuse that method with what's going on in Foo's getSpelling().

    public String getConstant() { return this.spelling(); /* !!! */ }

Then add to the demonstration method, main():

        f = Foo.CONSTANT1;
        System.out.println( f.getConstant() );

The additional output from this enhancement is:

    CONSTANT1

Course through all values in a loop

Traverse all values in the enum:

public enum Direction
{
   NORTH,
   NORTHEAST,
   EAST,
   SOUTHEAST,
   SOUTH,
   SOUTHWEST,
   WEST,
   NORTHWEST
}

for( Direction dir : Direction.values() )
{
    System.out.println( "  " + dir );
}

The output is:

    NORTH
    NORTHEAST
    EAST
    SOUTHEAST
    SOUTH
    SOUTHWEST
    WEST
    NORTHWEST

Determine if a value is contained without looping

Use Java's enumeration mechanism to avoid looping through looking for queue keys. Let's imagine you were using keys like "activemq" and "amazonsqs" in a properties file. Imagine someone added a property, but got it wrong.

public enum QueueType
{
    ApacheActiveMQ ("activemq"),
    AmazonSQS      ("amazonsqs"),
    ComcastCMB     ("comcastcmb"),
    OracleAQ       ("oracleaq");

    private String queueKey;

    private QueueType( String queueKey )
    {
        this.queueKey = queueKey;
    }

    public String getQueueKey()
    {
        return this.queueKey;
    }

    public static boolean contains( String queueKey )
    {
        try
        {
            QueueType.valueOf( queueKey );
        }
        catch( IllegalArgumentException e )
        {
            return false;
        }

        return true;
    }

    private static final String[] keys = { "activemq", "oracleaq", "grandpa's attic" };

    public static main( String[] args )
    {
        QueueType qt;

        for( which = 0; which < keys.length; which++ )
        {
            if( QueueType.contains( keys[ which ] ) )
                System.out.println( keys[ which ] );
            else
                System.out.println( "There is no " + keys[ which ] );
        }
    }
}

The output is:

    activemq
    oracleaq
    There is no grandpa's attic

Appendix: the complete enum Foo class

After the additions to the original, simple example, this is what I have. It's useful to study the additional print statements not discussed above to see their effect: you don't have to implement getOrdinal() or getConstant() since these are for free.

package com.etretatlogiciels.examples.enums;

public enum Foo
{
    CONSTANT1 ( 1, "One" ),
    CONSTANT2 ( 2, "Two" );

    private int    ordinal;
    private String spelling;

    private Foo( int ordinalvalue, String spelling )
    {
        this.ordinal  = ordinalvalue;
        this.spelling = spelling;
    }

    public int    getOrdinal()  { return this.ordinal; }
    public String getSpelling() { return this.spelling; }
    public String getConstant() { return this.name(); /* !!! */ }

    public static Foo findBySpelling( String spelling )
    {
        for( Foo foo : Foo.values() )
        {
            if( spelling.equals( foo.getSpelling() ) )
                return foo;
        }

        return null;
    }

    public static boolean contains( String spelling )
    {
        try
        {
            Foo.valueOf( spelling );
        }
        catch( IllegalArgumentException e )
        {
            return false;
        }

        return true;
    }

    public static void main( String[] args )
    {
        Foo f;

//        f = Foo.valueOf( "One" );     (don't crash!)
        f = Foo.valueOf( "CONSTANT1" );
        System.out.println( f );
        System.out.println( f.getOrdinal() );
        System.out.println( f.ordinal );
        System.out.println( f.getSpelling() );

        f = Foo.findByName( "Two" );
        System.out.println( f );

        final String forty_two = "Forty-two";
        if( Foo.contains( forty_two ) )
            System.out.println( forty_two + "is a recognized spelling" );
        else
            System.out.println( forty_two + "is not a recognized spelling" );

        f = Foo.CONSTANT1;
        System.out.println( f.getConstant() );
        System.out.println( f.name() );
    }
}

The final output is:

    CONSTANT1
    1
    1
    One
    CONSTANT2
    Forty-two is not a recognized spelling
    CONSTANT1
    CONSTANT1