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.
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
- Gaʀʀʏ’s comment: switch instanceof?
- “Effective Java”, Second Edition, by Joshua Bloch; “Item 20: Prefer class hierarchies to tagged classes”.
- Java Vs Groovy – Enum Demo
- Switch Statements with Enums best practice
- “Effective Java”, Second Edition, by Joshua Bloch; “Item 30: Use enums instead of int constants”.
- Groovy Goodness: Transform String into Enum
- Avoid switch! Use enum!
