preload
Feb 27

One part of reviewing and refactoring code is finding and removing code smells. An example, which I encounter quite often is the outmoded manner of coding Enumeration-like behaviour using int-constants. Or worse: String constants. The java.util.Calendar-class serves as a great example for this former best practice, before the Enumeration-Type was introduced with Java release 1.5.

Imagine you want to get a java.util.Date object pointing to the 1st of January 2009, midnight. This is one way of doing it:

Calendar cal = Calendar.getInstance();
cal.set(Calendar.YEAR, 2009);
cal.set(Calendar.MONTH, Calendar.JANUARY);
cal.set(Calendar.DAY_OF_MONTH, 1);
cal.set(Calendar.HOUR_OF_DAY, 0);
cal.set(Calendar.MINUTE, 0);
cal.set(Calendar.SECOND, 0);
cal.set(Calendar.MILLISECOND, 0);
Date myDate = cal.getTime();

This has been a clean way of implementing and “imitating” enum-like behaviour in pre 1.5 times and of course in projects that require a lower java version.

Since we’re passing ints to the set method, it is possible to do something like this, without having the compiler complain about it (seen it done before):

cal.set(Calendar.MONTH, 1);

And due to the fact that the number of the months in the Calendar class starts with 0 this would create a Date object pointing to the 1st of February. Ouch.  An even more evil way of misusing the Calendar class could be this:

cal.set(2, 1);

I’ve never seen this done before, but looking at it, I must say it really hurts.

Since the release of Java 1.5 there is a much better way of programming that kind of behaviour using the Enumeration type. This is how a simple Month enumeration could look like:

public enum SimpleMonthEnum {
        JANUARY, FEBURARY, MARCH, APRIL, MAY, JUNE, JULY, AUGUST, SEPTEMBER,OCTOBER, NOVEMBER, DECEMBER;
}

But we can add a little more brain to the implementation:

We’ll add member variables for the name, index and number of days, a constructor with arguments for each of these values and we’ll provide getters for retrieving them. Additionally we’ll add a method for calculating the correct number of days depending on a given year, considering leap years.

package com.coredump.enumTest;

public enum MonthEnum {
       
        // define the items of the enumeration
        JANUARY("January", 1, 31),
        FEBRUARY("February", 2, 28),
        MARCH("March", 3, 31),
        []
        NOVEMBER("November", 11, 30),
        DECEMBER("December", 12, 31);
       
        // member variables
        private String name;
        private int index;
        private int numberOfDays;
       
        // constructor
        private MonthEnum(String name, int index, int numberOfDays) {
                this.name = name;
                this.index = index;
                this.numberOfDays = numberOfDays;
        }

        public String getName() {
                return name;
        }

        public int getNumber() {
                return index;
        }
       
        public int getNumberOfDays() {
                return numberOfDays;
        }
       
        public int calcNumberOfDays(int year) {
                // check for leap years:
                // year modulo 4 is 0
                // if year modulo 100 equals 0
                // and year modulo 400 is not 0, it’s not a leap year
                if (this.equals(MonthEnum.FEBRUARY) && year%4 == 0
                                && !(year%100 == 0 && year%400 != 0)) {
                        return this.numberOfDays +1;
                }
                return this.numberOfDays;
        }
}

And here’s an example of how the enumeration can be used:

public class MontEnumClient {
        private static void printMonthDetails(MonthEnum monthEnum) {
                System.out.println(monthEnum.getName());
                System.out.println(monthEnum.getNumber());
                System.out.println(monthEnum.getNumberOfDays() );
        }
       
       
        public static void main(String[] args) {
        System.out.println(MonthEnum.FEBRUARY.calcNumberOfDays(2008));
        System.out.println(MonthEnum.FEBRUARY.calcNumberOfDays(2009));
        System.out.println(MonthEnum.FEBRUARY.calcNumberOfDays(1700));
        System.out.println(MonthEnum.FEBRUARY.calcNumberOfDays(1600));
       
        printMonthDetails(MonthEnum.DECEMBER);
        }
}

I warmly recommend the use of this great feature of the Java language. It definitely adds more type safety, ease of use and security to your application. But unfortunately it seems to me, that it still hasn’t reached the popularity that it really deserves, yet.

Tagged with: