Java 1.5+ 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 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
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 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.
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.
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.
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.
- 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 in addition to the enum
constant name.
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.
Learning More
Sun’s JDK Technote Guide on
enums : available:
Sun’s Javadoc on the
Enum, the class underpinning enums class : available:
Sun’s Javadoc on the
EnumMap class : available:
Sun’s Javadoc on
Class.
getEnumConstants : available:
To Come
inner class gotchas