Wednesday, September 21, 2011

Ranges and Sequences in Java Interface

Friends,

We often come across requirements where we have to create interfaces to define contiguous constants or a sequence of constants in an interface may be because they represent array indexes.
But such a code becomes hurdle when we need to add one or more constants in-between the existing list of contiguous constants.

e.g. in the following code snippet:


public interface XYZConstants
{
      public static final int A1 = 0;
      public static final int A2 = 1;
      public static final int A3 = 2;
      public static final int A4 = 3;
      public static final int A5 = 4;
      public static final int A6 = 5;
      public static final int A7 = 6;
}

If you want to add say A31, A32 and A33 between A3 and A4, you will need to modify the values of all the constants after A3 as shown below:



public interface XYZConstants
{
      public static final int A1 = 0;
      public static final int A2 = 1;
      public static final int A3 = 2;
      public static final int A31 = 3;
      public static final int A32 = 4;

      public static final int A33 = 5;
      public static final int A4 = 6;
      public static final int A5 = 7;
      public static final int A6 = 8;
      public static final int A7 = 9;
}

This is fine if the list is small but what if the list has 5000 constants and you want to add after 1500 and you can't just append at the last because the code will become haphazard ?

How nice it would be if we can just increment some integer counter or sequence but the problem is that interfaces do not allow arithmetic operations in the assignment and only allow fields to be public static and final.

Following is one possible solution:

package test;

/**
 * @author ujariya
 *
 */
public interface InterfaceCounter
{
      public static final int A1 = Counter.nextVal(0,10);
      public static final int A2 = Counter.nextVal();
      public static final int A3 = Counter.nextVal();
      public static final int A4 = Counter.nextVal();
      public static final int A5 = Counter.nextVal();
      public static final int A6 = Counter.nextVal();
      public static final int A7 = Counter.nextVal();
     
      public static final int A8 = Counter.nextVal(200, 300);
      public static final int A9 = Counter.nextVal();
      public static final int A10 = Counter.nextVal();
      public static final int A11 = Counter.nextVal();
      public static final int A12 = Counter.nextVal();
      public static final int A13 = Counter.nextVal();
      public static final int A14 = Counter.nextVal();
      public static final int A15 = Counter.nextVal();
     
      class Counter
      {
            private static int count_ = 0;
            private static int max_ = Integer.MAX_VALUE;
           
            public static int nextVal()
            {
                  int ret = count_++;
                 
                  // The biggest flaw in this approach is, if the range exceeds,
                  // it will only be known at execution time when this class
                  // gets loaded in the JVM
                  // There is no Compile Time Catch :-)
                  if(ret > max_)
                        throw new RuntimeException(
                                    "Counter exceeded out of range: "+ret);
                 
                  return ret;
            }
           
            public static int nextVal(int seed, int max)
            {
                  count_ = seed;
                  max_ = max;
                  return count_++;
            }
      }
}

Here, we have a inner class Counter which statically keeps counting the next value and we get the contiguous range of constants in every case.

In the aforementioned example, you will just need to add the following lines wherever you like to add:

      public static final int A31 = Counter.nextVal();
      public static final int A32 = Counter.nextVal();

      public static final int A33 = Counter.nextVal();

This logic will guaranty that you always get a sequence no matter where you have added the new lines of code.

You can also maintain the ranges if you want but if your range gets exceeded, it can be identified at run-time only because the values will get calculated while loading the class in the JVM.


Regards,
~Upendra.


P.S.

If you wanna test the above logic, following is the testing code and test results:

public class InterfaceCounterTest
{
      public static void main(String[] args)
      {
            System.out.println(InterfaceCounter.A1);
            System.out.println(InterfaceCounter.A2);
            System.out.println(InterfaceCounter.A3);
            System.out.println(InterfaceCounter.A4);
            System.out.println(InterfaceCounter.A5);
            System.out.println(InterfaceCounter.A6);
            System.out.println(InterfaceCounter.A7);
            System.out.println(InterfaceCounter.A8);
            System.out.println(InterfaceCounter.A9);
            System.out.println(InterfaceCounter.A10);
            System.out.println(InterfaceCounter.A11);
            System.out.println(InterfaceCounter.A12);
            System.out.println(InterfaceCounter.A13);
            System.out.println(InterfaceCounter.A14);
            System.out.println(InterfaceCounter.A15);
      }
}

And the test results are:

0
1
2
3
4
5
6
200
201
202
203
204
205
206
207
 

No comments: