Java version 1.5 or later has built-in support for enumerated types. Prior to that you had to kludge them in some way with static final ints (which
were not typesafe) or with Objects which would not work in switch statements.
Enum Basics
Don’t confuse enum with the Enumeration
interface, an almost obsolete way of iterating over Collections, replaced by Iterator.
Java version 1.5 enums are references to a fixed set of Objects than represent the various possible choices. enums handle
single choices, not combinations of choices. For combinations, you need EnumSet. For HashMap-style lookups when the key in an
enum, you need EnumMap.
Enum and the enum constants are just classes. Since
enum constants are constants, normally they should be all capitalised. However,
since the constant name is the same as what is displayed externally, some people relax that rule and name
constants with upper or lower case names as appropriate for the application. Note that you can’t call your
enums default or null
since these are reserved words. Try usual or none.
enums you want widely known should be put in the their own source file and given
the attribute public. You then import the enum, as you
would any other class.
Needless to say, enums are type safe. If you try to store an enum constant for a dog Breed into a Locomotive type enum variable, the compiler will refuse.
Sample Code using enum
Advantages
The advantages of enums over an array of Strings are:
- You can use enums as case labels.
- Methods come built in with enums to do do things like convert enums names to ordinals, and combos with enumset.
- You can attach additional fields and code to enum constants.
- enums are type safe. With Strings all your items in all categories are the
same type. There is nothing to stop you from feeding a fruit category to an animal parameter.
- You can compare enums quickly with ==. You
don’t need to use equals as you do with String.
Disadvantages
The disadvantages of enums over an array of Strings are:
- You can’t create new enum constants at run time. You must recompile. In
contrast, it is easy to add another String to an array or ArrayList.
- You have to create en entire new class for each enum.
- You can’t create derived enum classes with a few extra enum constants. You can fairly easily add a few more Strings to a List.
Enum Constants Can Have Common Methods
In this simple example, the enum constants each have an instance field
colours and an instance method getColours.
To & From Ordinal
Breed dog = Breed.LABRADOR;
int ordinal = dog.ordinal();
int ordinal = 2;
Breed dog = Breed.values()[ ordinal ];
Built-in Methods
All enums and enum constants have some built-in
methods:
Enum Variables
Enums can have static variables shared between all enum constants and instance
variables
Enum Constants Can Have Individual Methods
There are three basic ways to write an method for use by an enum constant:
- Single common method for all enum constants. It works with different constants
passed in the constructor. The method is part of your enum class as a whole.
- An abstract or default method in the enum class
as a whole, and each enum constant overrides it.
- enum constants have peculiar methods all their own. If two enum constants implement the same method, they are only incidentally related.
The individual enum constants with methods are implemented as and behave like little anonoymous classes. That is
how the indivdidual methods can override methods common to the enum as a whole, and how the enum methods have
access to the fields of the enum as as whole as well as their own fields. Further the scope rules of visibility
and how the scope modifiers such as public and private
work the same way as for anonymous inner classes.
Getting Fancy
enum constants can even each have their own little anonymous inner class complete
with constructors, methods and variables. See the Under the Hood (3) example below.
I use enums for writing the finite state automata that colourise all the listings
on my website. Each state is represented by one enum constant. Each enum constant has custom logic to figure out the next state. The enum as a whole has both static and instance methods, and the enum
constants override some of the instance methods. That allows a default implementation for a method that all
enum constants must implement. I can invoke the method by the common base name, just
as with classes and subclasses.
It is easiest to use public or top-level enums. Begin your experiments with
public enums in their own *.java file to avoid frustration. I have managed to get nested static
enums and nested inner enums to work as well, at least
for the simple case where the enum constants have no constructors or individual
classes. Even when you don’t specify static, nested enum classes are considered static. They don’t require an
associated outer class object. There are no such thing as anonymous enums, and if
there were, they would not be very useful since their enum constants would be
anonymous too.
Unlike most of Java, you need to understand how enums work under the hood. When
you understand the inner class structure, everything makes sense. Without that understanding enums will drive you insane with bizarre behaviour and restrictions.
Nested enums
An enum is a species of class, so it can be a public
class, a top level default class or a nested static class. Inner class enums are considered static even if you don’t use the
static keyword. An enum follows the same scope and
visibility rules as any other class.
I recommend using only public enums and putting them
each one in its own *.java file. However, you can also create toplevel enums, and static
enums like this:
All enums support both static and instance fields
and methods of the enum as a whole. However, enum
constants are fields that point to unique objects of the enum class with optional
anonymous inner classes. They can thus include instance fields and methods, but not static, simply because for some idiotic reason all inner classes can’t support
static fields and methods. Some believe the reason statics are not implemented was
not laziness but rather a desire prevent programmers from using inner classes for purposes other than serving the
outer class. One speculated the problem comes because there are actually two flavours of anonymous classes, those
declared in a static and an instance context. Another explanation is that you run into chicken and egg
initialisation problems if you allow static members.
Under The Hood (1)
Here is a simple program using enums and a switch. I
have provided a commented decompilation so that you can see how enum pulls off its
magic.
Here is a decompilation of the above code. Note how the Breed enum is, underneath, just an ordinary class that extends the abstract class
java.lang.Enum<E extends Enum<E>>.
I wondered wonder why Sun didn’t just use the enum ordinals directly as case
numbers. The tableswitch JVM instruction does an indexed lookup, which would be one
level of indirection less than Sun’s enum scheme. Perhaps Sun was worried the
compiler would sometimes generate a slower lookupswitch if the table were not dense or
if it were large. With their preconditioning, they guarantee the table is both small and dense, which guarantees
that the faster tableswitch instruction will be used. Sun could refine its
technique by collapsing the lookupswitch table further by using the same case
number for multiple case labels on the same code. Others have pointed out Sun’s indirect scheme provides
additional binary compatibility. If class A has a switch
statement involving enum B, and B acquires a new enum constant, even one in the middle of the
enumeration order, and A is not recompiled, the switch
will still work (treating the new enum constant as part of the default category.
Under The Hood (2)
Here is a simple program using enum where the enum
constants all use the same implementation of the same method and same instance field. I have provided a commented
decompilation so that you can see how enum pulls off its magic.
Under The Hood (3)
Here is a simple program using enum where the one enum
constant implements a method that the other enum constants do not. I have provided a
commented decompilation so that you can see how enum pulls off its magic.
Serialization
Enums can be serialized. The usual serialization process is not sufficient since it
would result in an enum variable being reconstituted to point to a clone of the
enum constant from the source machine rather than to the corresponding enum constant in the target machine. The serializaton process gets around this problem by
serialising as the enum constant name (a String), along
with information identifying its base enum type. Deserialization behavior differs as
well — the class information is used to find the appropriate enum class, and
the Enum. valueOf method is called with that class and the
received constant name in order to obtain the enum constant to return.
This technique makes serialized files more stable that had it been based on using ordinals to represent the
value. With Sun’s technique you can add new enum constants, or change the
enum methods, and you can still read old serialised files with new code.
The Good About Enums
- enums are much more powerful the C++
enum. It is a whole new way of writing code.
- enums are type safe.
- You don’t have to manually assign ordinals. They are self-numbering.
- The external form is usually a String rather than a number. This means even if
your ordinals change, your old data files are still valid.
- enums save you typing the class name on the enum
constant as case labels.
- enums let you consolidate all facts about each enum constant in one place. As you add methods to your enums,
most of your ugly switch code disappears. This encapsulation makes code easier to
maintain, and makes the client code that uses your enum simpler.
- enums are flexible. They are like classes with the extra wrinkles of an
automatic ordinal and valueOf method.
The Bad About Enums
- Unlike using simple ints for your enum logic,
everything goes into its own separate enum class. This makes accessing surrounding
class methods and variables somewhat more awkward.
- enums don’t nest with inheritance. You can’t extend an enum. You can’t extend an enum to either add more
enum constants or more static methods or more
instance methods on the enum constants, or more instance datafields on each
enum constant, or a more elaborate constructor. There is no way to extend an
enum with fewer enum constants. Here are a couple
of kludges to extend enums.
- Pass an enum constant delegate of the base enum to the constructor for the corresponding enum in the
extension. Then use wrappers to make all the methods of the base enum available
in your new enum. You can see this technique used in the Replicator’s enums.
- Don’t use enums at all, but instead create an Item class and instantiate an array of such Items each with
custom values in the constructor. These behave somewhat like enums, except they
don’t work in switch statements. You can extend the Item class, create new
Items, create subsets of existing Collections,
iterate over all Items in a Collection etc.
- The expressions in your enum constant constructors all must have values known
at the time the enum class is loaded. In traditional coding, those values don’t need to be known until
the methods are evaluated.
- In the simple case, an enum is a class derived from Enum (capital e), with a set of static final objects of that same class, one for each choice possibility. In a more complex case,
each enum constant object could be instantiated from a different subclass derived
from the your base class common to all the constant objects, each constant object with its own private
variables and methods. The structure boggles the mind. You need to re-learn the class/instance/reference model
all over in enum terms.
- It is confusing with all these enum and enum
constant instances, just how many objects there are, and which fields are shared with which other objects. The
underlying class structure is somewhat hidden.
- The biggest problem I have found is when you declare your enum class inside
another class, it becomes an ordinary anonymous inner class, and thus you cannot have static fields in your enum constant anonymous classes or but you
can in the enum itself. However, these enum statics are not much use since you
can’t access them in the constructor. Here is a workaround, suggested by Piotr Kobzda.
The
describes yet another work around.
- You are not allowed to access any static fields anywhere in your custom
enum constant constructors. Why? I don’t know. This is a real annoyance if
you want constructors to build lists accessible to all the enum constants, such as
lists of aliases.
- enum constructors cannot access the enum
static fields. However, they can invoke the enum’s static methods that access the static fields. The problem is static initialisation has not
been done at the time the enum constructor constant constructers are invoked, so
using static methods will just see zeros. Constructors can also directly access
static final in-lineable constants known at compile time. I asked, why does not
Java just do the static initialisation before running the enum constant constructors? The problem then would be the static initialisation code would not be able to reference the enum constants. You have an intractable chicken and egg problem. The
describes some work arounds. One work around is to keep your enum constructor very simple and do your initialisation later with an explicit static inititialise method.
- Older IDEs don’t understand the enum syntax. It confuses them and screws
up your usual ability to tidy, look up references etc.
- The implementation was driven by the desire for making no changes to the 1.4 JVM. This means they are
slower and kludgier than they could have been. I would have preferred a direct switch lookup rather than the two-stage one used now.
- You have to deal specially everywhere with the possibility of a null value. It
is not a legitimate case label and it does not count as a possibility for routing to default. With ints, int choice = 0; is just another
case. Under the hood the JVM has to check for null and throw a NullPointerException just before every enum switch.
- the assignment of ordinals to enums depends on the order you declare them.
Many IDEs will reorder code, thus possibly breaking applications. Your datafiles might contain embedded
ordinals which will no longer map to the same enum. So you should use short names
to represent enum values databases or permanently assigned numbers, just the way you did back in the days of
C++.
- Though you can write methods peculiar to an given enum constant, you
can’t invoke them. You can only use them within the enum inner class.
- You can’t declare a variable to be of the same type as some enum
constant. Their class names are anonymous. The names of the enum constants are
references, not classes. This is a Good Thing™. Otherwise you would have an explosion of classes, one for
every enum possibility.
- Because you must put your static finals for an enum at the end, you can’t use them in invoking the constructors for the enum constants. You have to put them in some other class.
- valueOf supports only one name (without aliases) for the external Strings to represent each enum constant enum, and it must exactly match the interval variable
name, usually all caps. Normally, you want at least both upper and lower case variants, and perhaps a few
aliases to be forgiving. You also want short names (one or two chars for databases) and long names for humans,
and localised names. If you want any of this, you must roll it yourself.
- You can’t have forward references to enum constants. So, for example,
you can pass enum constants that are defined later, as parameters to enum constructors to create interrelationships between enum
constants.
Alias Values
Here is a simplified version of a program that lets you create a valueOf that accepts
aliases
Data Bases
How do you deal with storing enums in databases? Here are ways I would not
recommend:
- Serialising them. Not good for long term storage, also bulky. You may not change any of the enum constant names without obsoleting your serialised data.
- Storing them as the program enum name, in either upper or lower case. Bulky.
- Storing them as an internationalised String. I hope the utter folly of this is
obvious.
- Storing them as ordinals. The fragility of ordinals is a side effect of the lack
of need for explicit assignment as you did with C++ or the old style manual
Java enums. The problem is when an enum is added or removed, it shifts all the numbers.
- If you delete an enum constant, the subsequent ones will be renumbered.
- If you insert an enum constant, the subsequent ones will be renumbered.
- If you keep the enums in alpha order they will be reordered if any enum is renamed.
- If you ask a tidy program to clean up you enums, they will be renumbered.
Any time the ordinals are disturbed, for even the tiniest change, you must convert your database to the new
numbering scheme, not just the database schema The advantage of ordinals is they are compact, especially when
you store only the 8 low order bits.
- Store them as one-character ASCII. You may run out of meaningful codes for large enums.
- Store them as short multi-character strings. Keeping the strings short is important when there are millions
of records containing them.
Here are ways I would recommend:
- You can use a permanent ordinal. You start out with ordinals, then as old values are deleted, you
don’t reuse the number, and you always add new ones on the end, irrespective of what Java assigns for
ordinal. You must get your database to translate your permanent ordinals to Java ordinals with its enum
feature. This mimics the way C++ handle the problem, and makes it easy to
share your database with C++ programs.
- Store them as two-character ASCII strings. This approach I recommend. Fixed length saves
database space, as does 7-BIT ASCII. It is reasonably compact, and the databases don’t need to be
converted when you add or remove enums. You don’t have to run a conversion program on a database, make
sure it is run only once, or deal with what happens if an old database is restored from backup that may need
several consecutive conversions with obsolete code. MySQL will happily interconvert these 2-character codes to
your current enum ordinals or enum names easy conversion to and from enum.
In your enum, you have a valueOf variant that accepts
the database 2-char abbreviation using a hash-map constructed at load time from values() list. The 2-char names are defined in the enum constant
constructors. This allows you to rapidly interconvert between enum ⇔ 2-char ⇔ internationalised String.
Storing invariant two-char codes in your SQL database will protect you from the main catastrophic error
— out by one on your enum constants without noticing.
If a database contains an invalid 2-char code, you will detect the problem the instant your code encounters
it. The error won’t stealthily skulk under the murk the way using ordinals permits.
Don’t use default in your switches. Then the
compiler will notify you if you did not insert code to handle the new enum case.
If you use Applets, your client code can convert 2-char enums to internationalised
strings. If you don’t, then your Servlets can do it. This should be considerably
faster than getting your SQL database to do it by table look up.
Legacy
Enums arrived rather late in the game, not until Java version 1.5. By this point, most of the
Java class library was already established. This is why it so rarely uses enums. It
uses a mishmash of named int constants instead, e.g. Calendar. Unfortuntately, these have no type safety, and they are not organised in to
functional groups. Enums are used in some of the more recent classes such as java.util.concurrent. TimeUnit and java.lang.annotation. ElementType.
General Purpose Enum Code
Writing general purpose code to work an arbitrary enums is quite tricky. You need to
use generics deftly and play with class objects. Part of the problem is passed
parameters cannot access the static methods of the enum
as a whole. Here is an example that will select a random enum constant from an
arbitrary
Learning More
Sun’s JDK Technote Guide on
enums : available:
Sun’s Javadoc on
Enum, the class underpinning enums : available:
Sun’s Javadoc on
EnumMap class : available:
Sun’s Javadoc on
Class.getEnumConstants : available:
Sun’s JDK Platform Guide to
serializing enums : available:
Sun’s Javadoc on
serialization form for enum : available:
Sun’s Javadoc on
Class.getEnumConstants : available:
To Come
inner class gotchas