Introduction
A useful feature added in Java 1.5 (also known as J2SE 5.0, 2004) is the enum
. In .NET, enum
s have been present since the very first version (2002, and as a beta since 2000) but the engineers at Sun managed to learn something from the shortcomings of the enum
s in .NET and provided more flexibility.
Let’s start with the simplest, the .NET implementation. In .NET, all data types including value types (equivalent of the primitive types) are part of the type hierarchy, being, indirectly inherited from System.Object
(equivalent of java.lang.Object
). The enum
s are just a specialization on top of exact numeric types, by default int (System.Int32)
. A typical declaration is as given below:
public enum Month
{
January,
February,
March,
April,
May,
June,
July,
August,
September,
October,
November,
December,
}
Notice that the compiler is forgiving and doesn’t complain that after the last element, we forgot to not place a comma. It will also work, of course, if we don’t place a comma after the last element. Behind the scenes, the compiler will generate a value-type inheriting from System.Enum
that will have 12 constants. By default, these constants will be of type Int32
and their value, again, by default, will start from 0
and increase by 1
for each member. January
will be 0
and December
will be 11
. Casts between the backing type (Int32
in this case) and the Months
type will be allowed both at design time and at runtime.
You can also force individual values for each member:
public enum Month
{
January = 3,
February = 33,
March = 222,
April = 14,
May = 20,
June = 23,
July,
August,
September,
October,
November,
December,
}
In this case, January
will be equal to 3
, February 33
, …, June 23
, July 24
(not specified but after a value-specified member, the next member will be the last value + 1
if specific value is not present. You can even force things into a bad situation like so:
public enum Months
{
January = 1,
February,
March,
April,
May,
June,
July = 1,
August,
September,
October,
November,
December,
}
Guess what, not only this is completely valid, but there won’t be just two duplicate values (January
and July
being equal to themselves, and equal to 1
) but also February
will be 2
, just like August
and so on. Of course, this is not recommended. The compiler and the runtime will happily apply your stupid scheme but the humans will be confused. This excess of freedom is not to my liking but I can’t do much about it except counter-recommend it. Ideally, you should not have to specify values for typical enum
s. Except for…
Flag-value enum
s (these are akin to Java’s EnumSet
):
[Flags]
public enum PizzaToppings
{
None = 0,
Mozzarella = 1,
Pepperoni = 2,
Anchovies = 4,
Mushrooms = 8,
Corn = 16
}
PizzaToppings myFavs = PizzaToppings.Pepperoni | PizzaToppings.Anchovies;
Console.WriteLine(myFavs);
This will not print “6
? although this will be the int
value of myFavs
. It will print “Pepperoni, Anchovies
”.
You’ll use the | (bitwise OR) to combine flags into a certain PizzaToppings
instance. To detect a certain flag exists, you can use any of these two ways:
bool hasAnchovies1 = (myFavs & PizzaToppings.Anchovies) != PizzaToppings.None;
bool hasAnchovies2 = myFavs.HasFlag(PizzaToppings.Anchovies);
The first one is uglier, old-school (pre .NET 4.0) but it’s completely type-safe. The second one is cleaner, more elegant but you can place an instance of another enum
in there. The compiler will not complain. Just the runtime which will throw an ArgumentException
.
Converting string
s into enum
instances and vice-versa is trivial also:
string s1 = "Anchovies";
string s2 = "AnCHOviES";
PizzaToppings pt1 = (PizzaToppings)Enum.Parse(typeof(PizzaToppings), s1);
PizzaToppings pt2 = (PizzaToppings)Enum.Parse(typeof(PizzaToppings), s2, ignoreCase: true);
PizzaToppings pt3 = (PizzaToppings)Enum.Parse(typeof
(PizzaToppings), s2);
(Note: typeof(PizzaToppings)
in C# is equivalent to PizzaToppings.class
in Java; the strange third parameter in pt2
is a named parameter. Optionally, you can include the parameter name followed by a colon before its value in order to improve the readability of the code. The compiler will “remove” this before compiling.)
Being seen like the backing-value-type by the runtime, you can very well use enum
s in a switch
:
switch(month)
{
case Month.January :
Console.WriteLine("Cold, brrr....");
break;
case Month.February :
Console.WriteLine("Still cold, brrr....");
break;
}
Being value-types take in account boxing and unboxing performance implications when assigning instances to object fields or variables and vice-versa (this is similar to the autoboxing issues with primitives in Java).
A few best practices are listed below:
- Consider singular names for simple
enum
s. For flagged enum
s, consider plural forms. - Include an “
Unknown
”, “None
”, “NotApplicable
”, etc. default value for each enum
. This will help you in case you forget to initialize a field of that enum
type, for example. You’ll be surprised by the default value and you’ll know (by a warning, exception, etc.) that the initialization did not take place. - Do not abuse them. If you find yourself having an instance of
enum
in a class and in most of the methods switching or if’ing over that field, then it might be a sign of a poor OOP design. Inheritance might be the key. Beware of switch! - In switches based on
enum
s, consider including a default clause throwing an out of range exception in order to detect unhandled newly-added enum
values.
Finally, let’s metion that you can also use byte
, short (Int16)
and long (Int64)
as backing type for your enum
s:
public enum Mood : byte
{
Happy,
Bored,
Sad,
Angry,
Ecstatic,
Sleepy
}
Let’s consider this enough, for now, in the .NET world and let’s take a look in Java.
Before Java SE 5, there was no support for enum
s. People were cramming static final public
fields (“constants
” as seen in .NET, or more precisely public static readonly
) in classes. There were and still are no static
classes so they were forced to create a private
constructor and even throw an exception in that private
constructor like so:
public class Mood {
private Mood() {
throw new IllegalOperationException();
}
public static final int HAPPY = 0;
public static final int BORED = 1;
public static final int SAD = 2;
public static final int ANGRY = 3;
public static final int ECSTATIC = 4;
public static final int SLEEPY = 5;
}
It, obviously sucked and the only improvement was to move all these (except the constructor) in an interface. Yes, in Java you may declare final
fields (“constants
”) in an interface. Not much better… just a tiny bit.
This was lousy since there was no kind of type safety. There was no printing support either. Switching on an int
and casing on these fields was a nightmare since you had no easy way to enumerate them and many more problems.
Final-ly (:P) Java SE 5 came out and the enum
support with it. This was quite impressing (note, my background is .NET development) – the enum
support. Here’s why:
- The
enum
types are reference types. This simplifies the null
management (yes, in .NET there is nullable types support but it’s a bit less intuitive than a reference type). - Because of the previous point, the
enum
s can be treated like any class. This means you can implement interfaces, add methods, override (non-final / virtual) methods. We’ll later see some interesting things on this. - We still get the same support for switching, parsing from strings, converting to
string
s and all others. EnumSet
helps with flagged enum
s. - You can have custom constructor(s) for your
enum
values.
There is no primitive type backing but that’s no loss. All enum
s inherit from java.lang.Enum
and can override toString()
. Each enum
value is a different instance of the enum
class, instantiated either with the default constructor either with a custom one. Here’s a full-fledged Java enum
sample:
public enum Planet {
MERCURY (3.303e+23, 2.4397e6),
VENUS (4.869e+24, 6.0518e6),
EARTH (5.976e+24, 6.37814e6),
MARS (6.421e+23, 3.3972e6),
JUPITER (1.9e+27, 7.1492e7),
SATURN (5.688e+26, 6.0268e7),
URANUS (8.686e+25, 2.5559e7),
NEPTUNE (1.024e+26, 2.4746e7),
PLUTO (1.27e+22, 1.137e6);
private final double mass;
private final double radius;
Planet(double mass, double radius) {
this.mass = mass;
this.radius = radius;
}
public double mass() { return mass; }
public double radius() { return radius; }
public static final double G = 6.67300E-11;
public double surfaceGravity() {
return G * mass / (radius * radius);
}
public double surfaceWeight(double otherMass) {
return otherMass * surfaceGravity();
}
}
By default, toString()
returns the same value as name()
but, as told earlier, it can be overridden. You can also obtain the ordinal of an enum
instance within the enum
with the ordinal()
instance method.
Although it’s a reference type, you can very well switch on an enum
in Java, without any problem:
switch(myPlanet) {
case EARTH :
System.out.println("Home.");
break;
Note that switch, in Java, does not require break (or any other control mechanism) for each branch. So you can easily (accidentally) fall-through and no compiler or runtime will save you.
All in all, I admire the better implementation of enum
s in Java and hope all platforms will have a better support in the future.