Java Enums unleashed

To learn more about enums (intoduced in Java 1.5) we must understand why they were needed in the first place.

Well I have managed to collate some info and stick it up here…hope this helps.

The idea behind enum is the ‘enum pattern’, which implies enumerated type as a type, whose legal values consist of a fixed set of constants.

1> The horrible ‘int Enum Pattern’:

[So some java guys (who earlier were C programmers) came up with the following solution for the ‘enum pattern’.]

// int Enum Pattern - has severe problems!

public static final int SEASON_WINTER = 0;

public static final int SEASON_SPRING = 1;

public static final int SEASON_SUMMER = 2;

public static final int SEASON_FALL   = 3;

This pattern has many problems, such as:

Not typesafe – Since a season is just an int you can pass in any other int value where a season is required, or add two seasons together (which makes no sense).

Example: If you have a method like   void storeSeason(int seasonType);

I may invoke it like            obj.storeSeason(8); //scary isn’t it!

And it also allows user to do .. SEASON_WINTER + SEASON_FALL

No namespace – You must prefix constants of an int enum with a string (in this case SEASON_) to avoid collisions with other int enum types.

Let’s say I have int Enums like

//For traffic signals

public static final int RED = 0;

public static final int ORANGE = 1;

public static final int GREEN = 2;

//For Fruits

public static final int MANGO = 0;

public static final int APPLE = 1;

public static final int ORANGE = 2;

you see the problem L

Brittleness – Because int enums are compile-time constants, they are compiled into clients that use them. If a new constant is added between two existing constants or the order is changed, clients must be recompiled. If they are not, they will still run, but their behavior will be undefined.

Printed values are uninformative – Because they are just ints, if you print one out all you get is a number, which tells you nothing about what it represents, or even what type it is.

2> Even worse ‘String Enum Pattern’

You may encounter a variant of this pattern in which String constants are used in place of int constants. This variant should never be used. While it does provide printable strings for its constants, it can lead to performance problems because it relies on string comparisons. Furthermore, it can lead naive users to hard-code string constants into client code instead of using the appropriate field names. If such a hard-coded string constant contains a typographical error, the error will escape detection at compile time and result in bugs at run time.

3> Then came the ‘type-safe enums’

[This was the OOP version of the ‘enum pattern’]

The basic idea is simple: Define a class representing a single element of the enumerated type, and don’t provide any public constructors. Instead, provide public static final fields, one for each constant in the enumerated type.

Here’s how the pattern looks in its simplest form:

// The typesafe enum pattern

public class Suit {

private final String name;

private Suit(String name) { this.name = name; }

public String toString()  { return name; }

public static final Suit CLUBS    = new Suit(“clubs”);

public static final Suit DIAMONDS = new Suit(“diamonds”);

public static final Suit HEARTS   = new Suit(“hearts”);

public static final Suit SPADES   = new Suit(“spades”);

}

Advantages:

– Because there is no way for clients to create objects of the class or to extend it, there will never be any objects of the type besides those exported via the public static final fields.

– Even though the class is not declared final, there is no way to extend it: Subclass constructors must invoke a superclass constructor, and no such constructor is accessible.

– As its name implies, the typesafe enum pattern provides compile-time type safety. If you declare a method with a parameter of type Suit, you are guaranteed that any non-null object reference passed in represents one of the four valid suits. Any attempt to pass an incorrectly typed object will be caught at compile time, as will any attempt to assign an expression of one enumerated type to a variable of another.

– Multiple typesafe enum classes with identically named enumeration constants coexist peacefully because each class has its own name space.

– Because typesafe enums are full-fledged classes, you can override the toString() method as shown earlier, allowing values to be translated into printable strings.

– Constants may be added to a typesafe enum class without recompiling its clients because the public static object reference fields containing the enumeration constants provide a layer of insulation between the client and the enum class.

What can possibly be any disadvantage ??

– Easiest would be that these enum constants cannot be used in switch statements.

– Also while coding/designing these classes one must keep lot of things in mind, which makes it error prone and a bit too verbose..let me give some more pointers

– Do you think Object.equals needs to be overridden?

Well there shouldn’t be any need for that.. as this pattern would anyways guarantee that only a single object represents each enum constant. So the Objects equal method should suffice (obj == otherObj).

– How to serialize/deserialize?

It is not sufficient merely to add implements Serializable to the class declaration. You must also provide a readResolve method.

This method, which is invoked automatically by the serialization system, prevents duplicate constants from coexisting as a result of deserialization. This maintains the guarantee that only a single object represents each enum constant, avoiding the need to override  Object.equals. Without this guarantee, Object.equals would report a false negative when presented with two equal but distinct enumeration constants.

– Work around to be done in case this class implements Comparable Interface.

– Performance (lets say while using collections for such type-safe enums) and Memory issues …(which unfortunately I too don’t know much about)

4> Java 5.0 : The dawn of the Enums

In 5.0, the Java™ programming language gets linguistic support for enumerated types. In their simplest form, these enums look just like their C, C++, and C# counterparts:

enum Season { WINTER, SPRING, SUMMER, FALL }

But appearances can be deceiving. Java programming language enums are far more powerful than their counterparts in other languages, which are little more than glorified integers.

–          The new enum declaration defines a full-fledged class (dubbed an enum type).

–          In addition to solving all the problems mentioned above, it allows you to add arbitrary methods and fields to an enum type, to implement arbitrary interfaces, and more.

–           Enum types provide high-quality implementations of all the Object methods.

–          They are Comparable and Serializable, and the serial form is designed to withstand arbitrary changes in the enum type.

As WINTER, SPRING, SUMMER, FALL are all static final objects of type Season

WINTER instanceof Season => true

Also, this is possible

interface SomeIntf{

enum Season { WINTER, SPRING, SUMMER, FALL }

}

Now knowing how Enum works, the following would make sense….

Many a times we would like to associate a value to an Enum constant.

Similar to..

public static final int PENNY = 1;   //where the constant is PENNY and the value is 1

Some may write it in the following way

enum Coin {

PENNY,

NICKEL,

DIME,

QUARTER;

}

public class TestEnum{

public static void main(String[] args){

Coin coin = Coin.PENNY;

System.out.println(“coin >> “+ value(coin));

}

public static int value(Coin coin){

switch(coin) {

case PENNY: return 1;

case NICKEL: return 5;

case DIME: return 10;

case QUARTER: return 25;

default: return 0;

}

}

}

Well the above code works fine…but it is not safe… the values should be tightly coupled with the enum constants.

Following are some ways to achieve it.

1>

//enum type with a method

public enum Coin {

PENNY,

NICKEL,

DIME,

QUARTER;

int value(){

switch(this) {

case PENNY: return 1;

case NICKEL: return 5;

case DIME: return 10;

case QUARTER: return 25;

default: return 0;

}

}

}

2>

//enum type with abstract type

public enum Coin {

PENNY {

int value(){ return 1;}

},

NICKEL {

int value(){ return 5;}

},

DIME {

int value() { return 10;}

},

QUARTER {

int value() {return 25;}

};

abstract int value();

}

3> //enum type with constructor

public enum Coin {

PENNY(1),

NICKEL(5),

DIME(10),

QUARTER(25);

private int coinValue;

int value() {return coinValue;}

Coin(int value){

coinValue = value;

}

}

How to use:

Coin coin = Coin.PENNY;

int val = coin.value();

Two classes have been added to java.util in support of enums: special-purpose Set and Map implementations called EnumSet and EnumMap. EnumSet is a high-performance Set implementation for enums. All of the members of an enum set must be of the same enum type. Internally, it is represented by a bit-vector, typically a single long. Enum sets support iteration over ranges of enum types. For example given the following enum declaration:

    enum Day { SUNDAY, MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY }

you can iterate over the weekdays. The EnumSet class provides a static factory that makes it easy:

    for (Day d : EnumSet.range(Day.MONDAY, Day.FRIDAY))
        System.out.println(d);

Enum sets also provide a rich, typesafe replacement for traditional bit flags:

    EnumSet.of(Style.BOLD, Style.ITALIC)

Similarly, EnumMap is a high-performance Map implementation for use with enum keys, internally implemented as an array. Enum maps combine the richness and safety of the Map interface with speed approaching that of an array. If you want to map an enum to a value, you should always use an EnumMap in preference to an array.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s