The Artima Developer Community
Sponsored Link

Let's Reconsider That
Java Enums want to be Classes
by Michael Feathers
September 20, 2005
Summary
Or do they?

Advertisement

For the longest while, I was glad that Java didn't have support for enumerated types. I've used them in C and C++ but I often ended up in that weird place that calls for the Replace Type Code with State/Strategy refactoring: I start writing a switch statement that uses the enum and I know that the code in the cases could be in a class, if only I had one instead of the enum.

A few days ago, I was working on some Java code that was going to use in a session at the Better Software conference in San Francisco. It involved navigating a player through a game. The simplest thing was to create an enum called Direction:

public enum Direction
{
    NORTH,
    SOUTH,
    EAST,
    WEST,
}

So far, so good.

I continued working and I noticed that one of the things that I really needed to do was find the opposite of a direction. If Direction was a class it would be easy to do it in a nice OO way, I could just add a getOpposite() method.

Man! My first attempt at using enums in Java 5 and I hit a wall. "But wait!", I said to myself. I remembered that enums can have instance fields and methods in Java:

public enum Direction
{
    NORTH,
    SOUTH,
    EAST,
    WEST;
    
    public Direction getOpposite() {
    	...
    }
}

The trick is figuring out what code to put in getOpposite().

Ideally, it would've been great to set the opposite of each Direction on construction and return that value from getOpposite(). Initially, it looked like that was possible, too. I found that you can pass arguments to enum values on construction. The syntax is a little odd:

public enum Direction
{
    NORTH(SOUTH),
    SOUTH(NORTH),
    EAST(WEST),
    WEST(EAST);
    
    Direction opposite;
    
    Direction(Direction opposite) {
        this.opposite = opposite;
    }
    
    public Direction getOpposite() {
    	return opposite;
    }
}

Unfortunately, this doesn't compile. The compiler tells us that the field SOUTH can't be referenced before it is declared. It looked like the only workaround was to do the following, and you have to admit it is pretty nasty:

public Direction getOpposite() throws DirectionException {
    Direction opposite;
    switch(this) {
        case NORTH: opposite = SOUTH; break;
        case SOUTH: opposite = NORTH; break;
        case EAST: opposite = WEST; break;
        case WEST: opposite = EAST; break;
        default:
            throw new DirectionException("Direction with no opposite:" + this);
    }
    return opposite;
}

The exception is particularly galling. Some statically typed languages make it a compile-time error when a switch on an enumeration doesn't cover all the cases. With Java, we're left with a possible runtime error. That doesn't seem to fit the spirit of the language.

After a bit more reading, I discovered a solution. We can have instance-specific methods on enumerations like this:

public enum Direction {
    NORTH { public Direction getOpposite() { return SOUTH; }},
    EAST { public Direction getOpposite() { return WEST; }},
    SOUTH { public Direction getOpposite() { return NORTH; }},
    WEST { public Direction getOpposite() { return EAST; }};
	
    public abstract Direction getOpposite();
}

It seems like the Java 5 designers handled the evolutionary change case well. You can start with an enum and move to classes pretty easily, but it makes me wonder whether it would've just been easier to start with enums as classes:

public abstract class Direction {}
public class North extends Direction {}
public class South extends Direction {}
public class East extends Direction {}
public class West extends Direction {}

and let them evolve into this:

public abstract class Direction {
    public abstract Direction getOpposite();
}

public class North extends Direction {
    public Direction getOpposite() { return new South(); }
}
...

Next time I feel the need for an enum, I might do that instead.

Talk Back!

Have an opinion? Readers have already posted 25 comments about this weblog entry. Why not add yours?

RSS Feed

If you'd like to be notified whenever Michael Feathers adds a new entry to his weblog, subscribe to his RSS feed.

About the Blogger

Michael has been active in the XP community for the past five years, balancing his time between working with, training, and coaching various teams around the world. Prior to joining Object Mentor, Michael designed a proprietary programming language and wrote a compiler for it, he also designed a large multi-platform class library and a framework for instrumentation control. When he isn't engaged with a team, he spends most of this time investigating ways of altering design over time in codebases.

This weblog entry is Copyright © 2005 Michael Feathers. All rights reserved.

Sponsored Links



Google
  Web Artima.com   

Copyright © 1996-2019 Artima, Inc. All Rights Reserved. - Privacy Policy - Terms of Use