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 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 an 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.
- Even though under the hood, an enum
is just a class, you can’t mix generics with enums. Further,
you can’t extend them.
- You can’t use constants defined in the enum
in arguments to the enum constructors.
- An enum can implement
an interface, but it cannot extend an
abstract class, class or another 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.
Inheritance
enums are rather limited in use of inheritance.
- An enum cannot extend another enum.
- An enum cannot extend a class.
- An enum can implement an interface.
- An enum can contain an abstract
method which is implemented by the enum
constants.
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:
Scope of enum Variables
enums can have static
variables shared between all enum
constants and instance variables. Nested enums can access the static
final constants and static
variables of the enclosing class, but not its instance variables. static and instance methods behave similarly.
Implicitly, a nested enum is a nested static class. Here is an example to
demonstrate these rules:
You cannot place static final
constants ahead of your enum
definitions. Neither can you place them afterwards and use the values
of those static finals in your enum
definitions. You have to place them in some other class.
Sorting enums
You sort an array of enum values the
same way you would an array of Object of some particular class. By
default, Java sorts by ordinal. You can define your own Comparators
as in this example:
You can of course also sort Collections
of enums in the obvious way. I have not
figured out how to implement Comparable
with enums, but even if I could, it
would probably not be wise to override the default. It would just
confuse people reading the code.
enum Constants Can Have Individual
Methods
There are three basic ways to write a 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 anonymous classes. That is how the individual 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 a 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.
Keying enums in a
GUI (Graphic User Interface)
Here is how to let the user select an enum
in a
GUI
using JComboBox.
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.
Gotchas
You cannot define static finals
in an enum and use them in the enum
constructors. You can put them in a separate private
static nested class.
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 (Java Virtual Machine)
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 would not be 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 serialization process gets around
this problem by serializing as the enum
constant name (a String), along with
information identifying its base enum
type. Deserialization behaviour 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 serialized files with new code.
Other ways of externalising enums for serialization or storing in
files and databases include:
- Converting them to integer ordinals. These are
extremely compact. One byte can encode 256
different possibilities. The problem is, if you add, delete, modify
or reorder your enums, you have to write code to convert your
existing files/databases. You convert them back to enums with values()[i].
- Converting them to upper case enum names These
are Strings. They are somewhat bulkier
that other methods. External files don’t usually need updating
if you add or reorder your enums. You convert them back to enums
with valueOf.
- Converting them to lower case letters. These are
compact, a 8 or 16
bits each. Normally you don’t have to modify your external
files when your enums changes. It makes the external files a little
easier to decode. You convert them back to enums with a switch
statement. The scheme falls apart if you have a large number of
possibilities, and many enums naturally map to the same letter.
- Save them in SQL (Standard Query Language)
enum format. This will be some proprietary format used by
your
SQL
database engine. Typically you can’t modify the enum without
changing the enum code both in your database, and in your programs,
and by writing a program to update your existing tables to the new
scheme. Typically you get the disadvantages of inflexibility
combined with the disadvantages of fluffy storage.
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.
- Let us say you had 10
static variables to count various things, and you display the
results at the end. You may find that making each an enum
with a count field, an incrementCount
and a getCount method, your code
simplifies and admits adding new features to each enum constant
easily, e.g. a description field. The code will me more
maintainable. It will be trivially easy to add another counter.
- enums can have a main
method, usually a debugging test harness.
The Bad About enums
- Even when an enum is nested in a
class, you cannot access the instance variables of the containing
class. You have to communicate instance information from the
enclosing class via parameters passed to enum
instance or static methods. Implicitly
a nested enum is a nested static
class, so this is what you would expect.
- 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 constructors 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 initialise
method.
- Older
IDEs (Integrated Development Environments)
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 a 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:
- Serializing them. Not good for long term storage, also bulky. You
may not change any of the enum
constant names without obsoleting your serialized 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 (American Standard Code for Information Interchange).
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. Unfortunately, these have
no type safety, and they are not organised into 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
Oracle’s Technote Guide on
enums : available:
Oracle’s Javadoc on
Enum, the class underpinning enums : available:
Oracle’s Javadoc on
EnumMap class : available:
Oracle’s Javadoc on
Class.getEnumConstants : available:
Oracle’s JDK Platform Guide to
serializing enums : available:
Oracle’s Javadoc on
serialization form for enum : available:
Oracle’s Javadoc on
Class.getEnumConstants : available:
Oracle’s Javadoc on
EnumMap class : available:
To Come
inner class gotchas