Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / All-Topics

Enum – Comparison of Java and .NET

5.00/5 (1 vote)
25 Jan 2018CPOL6 min read 6.2K  
Comparison of Enum in Java and .NET

A useful feature added in Java 1.5 (also known as J2SE 5.0, 2004) is the enum. In .NET, enums 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 enums 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 enums are just a specialization on top of exact numeric types, by default int (System.Int32). A typical declaration:

C#
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:

C#
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:

C#
public enum Months
{
    January = 1,
    February,
    March,
    April,
    May,
    June,
    July = 1,
    August,
    September,
    October,
    November,
    December,
}

Guess what, not only is this 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 enums. Except for…

Flag-value enums (these are akin to Java’s EnumSet):

C#
[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:

C#
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 strings into enum instances and vice-versa is trivial as well:

C#
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); // ArgumentException - 
                                                // by default the parsing does not ignore casing

(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 enums in a switch:

C#
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:

  • Consider singular names for simple enums. For flagged enums, 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 enums, consider including a default clause throwing an out of range exception in order to detect unhandled newly-added enum values.

Finally, let’s mention that you can also use byte, short (Int16) and long (Int64) as backing type for your enums:

C#
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 enums. 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 :

Java
public class Mood {
    private Mood() {
        throw new IllegalOperationException(); // prevent accidental instantiation even within the class
    }

    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 enums 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 strings and all others. EnumSet helps with flagged enums.
  • You can have custom constructor(s) for your enum values

There is no primitive type backing but that’s no loss. All enums 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:

Java
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;   // in kilograms
    private final double radius; // in meters
    Planet(double mass, double radius) {
        this.mass = mass;
        this.radius = radius;
    }
    public double mass()   { return mass; }
    public double radius() { return radius; }

    // universal gravitational constant  (m3 kg-1 s-2)
    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:

Java
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.

Implementing interfaces in an enum can be done in two ways: on each member or at the enum level:

Java
// at member level
public enum Chores implements Runnable {
    TAKE_OUT_THE_TRASH {
        @Override
        public void run() { // ...
        }
    },
    DO_THE_LAUNDRY {
        @Override
        public void run() { // ...
        }
    }
}

public enum FunStuff implements Runnable {
    GO_OUT,
    VISIT_NEW_PLACES,
    // ...

    @Override
    public void run() { // ...
    }
}

All in all, I admire the better implementation of enums in Java and hope all platforms will have a better support in the future.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)