Tuesday, September 30, 2008

Using Java “enums” in AnyLogic

Editor's note: A huge Thank You to Kevin Bennett, Senior Analyst in our San Diego office, for authoring this month's blog entry!

We all use constants in our code and models. In our simulation models, we typically use an all uppercase variable name, and assign it an integer value, so we can use it as an index in an array or use for comparisons. Here’s an example of what we might declare currently:

public static final int STATE_IDLE = 1;
public static final int STATE_CLASSIFYING = 2;
public static final int STATE_INSPECTING = 3;

This structure makes it easy to index into an array. For example, if you want to track statistics on a track, you could do this:

dTrackTimeInState[ STATE_IDLE ] += dTimeInState;


So what’s the problem? Well, constants have some basic issues when used this way:
  • Not type-safe - Since a State is just an integer you can pass in any other integer value where a State is required, or add two States together (which makes no sense).

  • No namespace - You frequently have to prefix constants with a string (in this case STATE_) to avoid collisions with other constants (i.e., TRACKSTATE_IDLE, ENGINESTATE_IDLE, CRANE_IDLE, etc.).

  • Printed values are uninformative - Because they are just integers, if you print one out all you get is a number, which tells you nothing about what it represents, or even what type it is.



Well, in the eternal search for something new and better, I started looking into Java enums. Similar to constants, but a bit more structured. What I found was that Java enums are very powerful!
The simple case would be to replicate what we did above:

public enum TrackStates
{
IDLE,
INSPECTING,
CLASSIFYING;
};

OK, so that doesn’t look very different, and doesn’t act too differently either. You can assign variables equal to the enums as before, although since the variable has to be of the enum type, it’s a little better documented and type-safe:

TracksStates iCurrentState;
iCurrentState = IDLE;
iCurrentState = 1; // FAILS TO COMPILE, BECAUSE iCurrentState IS NOT AN INTEGER.

So why use enums? Well sometimes the constants don’t work quite the way we want them to. For example, let’s say that you wanted to print out the state of each track. One way of doing this would be like this:

for( int i = 0; i < MAX_TRACKS; i++ )
{
switch iCurrentState[ i ]
{
case STATE_IDLE:
System.out.println( "Track " + i + ", state = IDLE");
break;

case STATE_CLASSIFYING:
System.out.println( "Track " + i + ", state = CLASSIFYING");
break;

case STATE_INSPECTING:
System.out.println( "Track " + i + ", state = INSPECTING ");
break;
}
}

OK, it works, but not too elegant. And what if you have 10 or 20 constants? Ugh. Let’s see what happens with enums:

for( int i = 0; i < MAX_TRACKS; i++ )
{
System.out.println( "Track " + I + ", state = " + iCurrentState [ i ] );
}

As Keanu Reeves would say, "Whoa." If iCurrentState[] is declared as a "TrackStates" variable array, then the enums know how to print their own value! And it won’t just print 1, 2, or 3, it will print the string "IDLE", "CLASSIFYING", or "INSPECTING"!

What if you don’t want to print the constant name? Suppose you want to print something different. That’s easy; just define the enum slightly differently:

public enum TrackStates
{
IDLE( "Track is Idle"),
INSPECTING( "Track is Inspecting"),
CLASSIFYING( "Track is Classifying");

String sName;
TrackStates (String name) { sName = name; }
public String toString() { return sName; }

};

OK, so you’ve added 3 more lines, but you only have to do that once, wherever you define your enums. Now when you print the state "IDLE", it will show up as "Track is Idle".

This also hints at an important fact about enums: they are actual Java classes. This means anything you can do in a Java class, you can do in an enum. That toString() method could easily be expanded to print anything you want based on system conditions.

It also means that you can add other properties and methods to your enums. Here a very important example. One downside to using enums is that, since an enum is a class, there is no default integer value for each element. So when you use an enum as an index into an array, you need to convert it to an integer value. There is a built-in method called ordinal() which will return the numeric order of the enum in its class. Unfortunately, it doesn’t look quite as clean as using a standard constant:

dTrackTimeInState[ IDLE.ordinal() ] += dTimeInState;

One thing that I’ve done in my last project is to make a new method called id(). This, at least, shortens the name I have to use when accessing arrays. In the enum, it looks like this:

public enum TestEnums
{
IDLE( "Track is Idle"),
INSPECTING( "Track is Inspecting"),
CLASSIFYING( "Track is Classifying");

String sName;
TrackStates (String name) { sName = name; }
public String toString() { return sName; }
public int id() { return this.ordinal(); }

};

dTrackTimeInState[ IDLE.id() ] += dTimeInState;

Here’s one more neat example. Suppose you have your Track States already defined in your model, and used everywhere. Maybe you need to initialize all of the Track State times at the beginning of the model. Previously you might loop through all the constants like this:

for( int i = 0; i < MAX_TRACK_STATES; i++ )
{
iTrackStateTime[ i ] = 0;
}


This is OK, but if you add a Track State, you need to make sure to go back and update the MAX_TRACK_STATES constant. Using enums, you can "short circuit" the For-loop, and there’s no need to update any constants:

for( TrackStates state : TrackStates.values())
{
iTrackStateTime[ state.id() ] = 0;
}

Not any shorter, but nothing to update if you add constants (other than the array size, which you’d have to do in either case). This also demonstrates the use of the Java equivalent of For-Each. The VB.NET version of that statement would look like this:

For each state in TrackStates
iTrackStateTime( state.id ) = 0

This turns out to be quite useful to allow you to iterate through enums easily.

This is just the start. Java enums can be very powerful, and almost limitless in their application. Try it in your AnyLogic model, and let us all know what new uses you come up with for enums!

Jim adds: Thanks Kevin! Enumerated types have been a useful programming construct -- it’s a shame that not all of the simulation languages have them. For example, while we’ve historically used Nicknames within Arena as a substitute for constants, we’ve had to rely on naming conventions as a substitute for true enumerated types.



I recommend using the enumerated type primarily to create a type-safe list of categorical values, and try to limit the amount of extra methods and member variables within the class itself. One thing Kevin and I discussed: Are there alternate ways of defining the dTrackTimeInState[] structure such that you don’t need to use the IDLE.id() method which converts it to an integer? Should we really be using the enum as an index into an array, or is there a better way?



For further background on enumerated types and their benefits, see http://en.wikipedia.org/wiki/Enumerated_type.