Quantcast
Channel: T. C. Mits 108 » java
Viewing all articles
Browse latest Browse all 36

Enum to Switch on Class types in Java and Groovy

$
0
0

The Java Switch statement is usually considered a code smell since it’s use can be eliminated by better use of Object Oriented Programming (OOP) design. Nowhere is this more apparent than with a related technique: attempting to switch on Class types by using the instanceof operator.

Real world
Unfortunately, in the real world, OOP is sometimes lacking, such as minimal interface based design, and refactoring, redesigns or sophisticated OO legerdemain cannot be used in a project. In this situation, one must resort to use of ‘switch’ or nested “if else”. This is a situation I recently encountered with legacy code that used if-else conditionals and instanceof since the target classes had no commonality. I was curious at the alternatives, so wrote up this blog post.

Important! The examples here do not reproduce the actual usage of the instanceof, wherein inheritance is taken account. See this Stackoverflow article for some info.

Groovy approach
In Groovy this is much easier since the switch statement supports much more types to switch on. In listing 1 below we simply use the classes directly. Note that this is not documented anywhere, afaik.

Groovy Example source
Listing 1

package com.octodecillion.experiment

class GroovySwithOnClass {

    static main(args) {
        
        switch(Class2.class){
            case Class1.class:
                assert false
                break
            case Class2.class:
                assert true
                break
            case Class2.class:
                assert false
                break
            default:
                assert false
        }    
    }
    
    public static class Class1{}

    public static class Class2{}

    public static class Class3{}

}

Now that is sweet and to the point!

Just to see if possible, one could explicitly switch on a Closure’s hashCode as follows:

class SwithOnClosure {

    static main(args) {
        Closure cl1 = {}
        Closure cl2 = {}
        Closure cl3 = {}
        
        switch(cl2.hashCode()){
            case cl1.hashCode():
                assert false
                break
            case cl2.hashCode():
                println "on cl2"
                assert true
                break
            case cl3.hashCode():
                assert false
                break
            default:
                assert false
        }
    }
    
}

For some more Groovy Enum goodness, the reading list at end gives some more examples.

Java Approach
In contrast to the Groovy used above, in Java things get more wordy. Below in listing 2 a JUnit class illustrates two methods of using a switch that acts on Class types.

Java 7
The method “testOnJava7″ shows that in Java 7 the switch can be on String values, so we just use the fully qualified class name (FQCN) as the case labels. This is not optimal, of course, any change to package or class names would break the switch.

Java 6
In Java 6, the ‘switch’ is more limited. In method “testOnJava6″ we create an Enum ‘Switcher’ that stores the FQCNs of the classes to switch on. To allow the conversion of class name to enum we have to implement the ‘fromString(String className)’ method (learned this in “Effective Java”).

Java Example source
Listing 2

package com.octodecillion.experiment;

import static org.junit.Assert.fail;
import java.util.HashMap;
import java.util.Map;
import org.junit.Test;

/**
 * @author josef betancourt
 */
public class SwitchOnClass {    
    @Test
    public final void testOnJava7(){
        
        switch(Class1.class.getName()){        
            case "com.octodecillion.experiment.SwitchOnClass$Class1":
                break;                
            case "com.octodecillion.experiment.SwitchOnClass$Class2":
                fail();
                break;
            case "com.octodecillion.experiment.SwitchOnClass$Class3":    
                fail();
                break;
            default:
                fail();                    
        }        
    }

    @Test
    public final void testOnJava6() {
        switch(Switcher.fromString(Class1.class.getName())){            
            case CLASS1:
                break;
            case CLASS2:
                fail();                
                break;
            case CLASS3:
                fail();
                break;                
            default:
                fail();            
        }        
    }

    /** enum type that stores the class names as field */
    public static enum Switcher{        
        CLASS1(Class1.class),
        CLASS2(Class2.class),
        CLASS3(Class3.class);    
        
        private static final Map<String,Switcher>classNameToEnum = 
             new HashMap<String, Switcher>();
        
        static{
           for(Switcher sw : values()){
                classNameToEnum.put(sw.className, sw);
            }            
        }
        
        /** convert class name to the enum type */
        public static Switcher fromString(String className){
            return classNameToEnum.get(className);
        }
        
        private String className;
        
        // constructor
        Switcher(Class<?> klass){
            this.className = klass.getName();
        }
        
    } // end enum Switcher
    
    public static class Class1{}

    public static class Class2{}

    public static class Class3{}    
}

Alternatives
1. Don’t do any of the above. Use instanceof to invoke a method specific to the target class. In this way future maintenance and possible OO refactoring can be done. This is possible with the Enum use too, but may be using the Enum for the wrong reason.

2. An alternative is to change the classes in question to partake in the enum use as shown by Gaʀʀʏ in a Stackoverflow article. This, imho, is not a good approach since it mixes concerns.

3. Still another approach was suggested by a colleague: switch on the hash code of a class, this would remove the maintenance of an Enum just for use of an instanceof replacement.

4. An interesting approach is to forgo the use of a switch altogether and just use the enum to invoke type specific behavior. A state machine? See “Avoid switch! Use enum!

Updates
Feb 13, 2014: Changed the format of the post.
Feb 15, 2014: added example with closures.

Environment

  • Windows 7
  • Eclipse 4.3
  • Java 7
  • JUnit 4
  • Groovy 2.2

Further reading

Creative Commons License
This work is licensed under a Creative Commons Attribution-NonCommercial-NoDerivs 3.0 Unported License.

Viewing all articles
Browse latest Browse all 36

Trending Articles