Monday, November 25, 2013

Using Java Enum As An Alternative to Bit Flags

Recently, I needed to use bit flags as parameter to a method to determine its output. I noticed while doing a bit of research online that most people suggest using EnumSet instead of bit flags as a parameter. Normally,  I use the bit flag like this:

1:  public class SampleUtil {  
2:       public static final int FLAG_1 = 1 << 0;  
3:       public static final int FLAG_2 = 1 << 1;  
4:       public static final int FLAG_3 = 1 << 2;  
5:       private static String processString1(String input) {  
6:            return input + "process1";  
7:       }  
8:       private static String processString2(String input) {  
9:            return input + "process2";  
10:      }  
11:      private static String processString3(String input) {  
12:           return input + "process3";  
13:      }  
14:      public static List<String> processInput(String input, int flags){  
15:            List<String> output = new ArrayList<String>();  
16:            if ((flags & FLAG_1) == FLAG_1) {  
17:                 output.add(processString1(input));  
18:            }  
19:            if ((flags & FLAG_2) == FLAG_2) {  
20:                 output.add(processString2(input));  
21:            }  
22:            if ((flags & FLAG_3) == FLAG_3) {  
23:                 output.add(processString3(input));  
24:            }  
25:            return output;  
26:       }  
27:  }  

For every flag set, I perform a method on an input and add it to the results. Works well but maybe things could be better.. so I decided to take a look at using enums. Taking another look at the docs, I remembered that enums are much more powerful and versatile than bit flags.

Since enum is also a class, I decided to exploit this property. One thing I noticed with the methods used for processing the string is that they all have the same input and output type. Another thing is that each one is tied to a certain flag exclusively. Because of this, I made an interface that represents all these methods.
1:  public interface IStringProcessor {  
2:       String processString(String input);  
3:  }  

Then, I modified the constructor to add a parameter for the enum:
1:       private IStringProcessor processor;  
2:       ProcessFlag(IStringProcessor processor) {  
3:            this.processor = processor;  
4:       }  

I also added a method to my enum class so I can use the processor passed on the constructor of the enum.
1:       public String applyMethod(String input) {  
2:            return processor.processString(input);  
3:       }  

Then I initialized the enums:
1:       FLAG_1(new IStringProcessor(){  
2:            @Override  
3:            public String processString(String input) {  
4:                 return input + "process1";  
5:            }  
6:       }),  
7:       FLAG_2(new IStringProcessor(){  
8:            @Override  
9:            public String processString(String input) {  
10:                 return input + "process2";  
11:            }  
12:       }),  
13:       FLAG_3(new IStringProcessor(){  
14:            @Override  
15:            public String processString(String input) {  
16:                 return input + "process3";  
17:            }  
18:       });  

To use enumset as a parameter, I modified my "processInput" method and used the enum type for processing my input string:
1:       public static List<String> processInput(String input, EnumSet<ProcessFlag> flags){  
2:            List<String> output = new ArrayList<String>();  
3:            Iterator<ProcessFlag> iterator = flags.iterator();  
4:            while (iterator.hasNext()) {  
5:                 ProcessFlag pFlag = iterator.next();  
6:                 output.add(pFlag.applyMethod(input));  
7:            }  
8:            return output;  
9:       }  

Here is a short code to demonstrate how to use the "processInput" method:
1:       public static void main(String[] args) {  
2:            EnumSet<ProcessFlag> flags = EnumSet.of(ProcessFlag.FLAG_1, ProcessFlag.FLAG_2);  
3:            List<String> strings = SampleUtil.processInput("Hello", flags);  
4:            for (String s : strings) {  
5:                 System.out.println(s);  
6:            }  
7:       }  

A complete source for this demo can be downloaded from here

No comments:

Post a Comment