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 generate an entire new class for each enum group and each enum option.
- You can’t create derived enum classes with a few extra enum constants or with a few suppressed. In contrast, 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 have your enums implement a common interface, but
they cannot extend a common abstract class.
- You can’t use constants defined in the enum class as arguments to the
enum constructors. The static finals must come from some other class, perhaps a nested
static.
- Be very careful using variables in enum constant constructors. The values must be
defined at the time the class is loaded. Sometimes the enum class gets loaded before you
expect and those values will be undefined or out of date. The precise time a class gets loaded is often
ill-defined or easily jarred.
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 ];
You cannot directly compare enum constant or variables. You must compare their
corresponding ordinals, e.g.:
boolean greater = Breed.DALMATIAN > Breed.DACHSHUND;
boolean greater = Breed.DALMATIAN.ordinal() > Breed.DACHSHUND.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
- When an enum is compiled, there is one class per enum constant.
However, the name of the enum constant does not appear in the class name. For example,
assuming the ordinal of DALMATIAN is 1, Breed.DALMATIAN would compile to Breed$2.class. Ordinals are 0-based. Class numbers are 1-based. That means to find the
corresponding class, you must add 1 to the ordinal. I think the class name should have been Breed$DALMATIAN.class. That would be easier to recognise in stack dumps.
- You cannot define a compareTo method because
Enum.compareTo() is final. It sorts enum constants
in definition order.
- 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 or a separate class. I find it works best to put nothing but
such static finals in the auxiliary class.
You cannot put references to the enums in your separate class of static
finals. They need to go in the main enum class, after the enum constants. One symptom is a NullPointerException starting in your
separate class, but occurring in the enum code. Initialisation must proceed in this order:
- Evaluate the static finals referenced in the enum
constructors.
- Evaluate the enum constructors.
- Evaluate the static finals that reference the enum
constants.
The simple way out is to turn your static init block involving enums into a static method, then invoke it with lazy initialisation
(postpone calling it to just before you need the initialisations).
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
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 Oracle’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 Oracle’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.
Encapsulation
What if you had three enum classes that shared a lot of identical code. How could you encapsulate it to avoid
the repetition?
- Enums cannot extend other enums, so that approach will not
work.
- Enums can implement an interface. That will help keep them
similar, but not identical.
- I have read that in Java 1.8+ that it is possible to have implementations in interfaces. That might be the way to go.
- I don’t think enums can extend an abstract class. Even
if they could, it would not ensure the code were identical, just similar.
- I have not yet experimented to see if enums can extend a class. You could put your implementation in a base class.
- Move the shared logic into a utility class (i.e. static methods)
- Move the shared logic into a dedicated type and use instances of this type as delegate in your enums.
- Reconsider if enums are the right choice. If you have a lot of common logic shared between different types
of enums, that’s an indication that enums are a poor fit for the underlying problem.
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 Oracle’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.
Enum Magic
The following snippet demonstrates Java’s amazing ability to keep track of different constants from
different enums, even when you call common code. It is not really a surprise when you understand an enum is just
an object and its methods are instance methods on its mini class.
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 Javadoc on
Enum, the class underpinning enums : available:
Oracle’s Javadoc on
EnumMap class : available:
Oracle’s Javadoc on
Class.getEnumConstants : 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