If you want to use a Java Enum as State pattern implementation you have at least two options where to write your state transition logic. In the first case you can put it directly into your Enum source file. This is advisable for less complex and simple logic. If your logic is more complex you should use one separate class for each state.
See the following examples for both possible approaches. The class Light is used as state context. By the way, using Java Enums is a good choice if you want to store the object state as field of a JPA entity.
public class Light { private LightState state; public Light(LightState state) { this.state = state; } public void toggle() { state = state.toggle(); } }
All-in-one Enum approach
public enum LightState { OFF { public LightState toggle() { return ON; } }, ON { public LightState toggle() { return OFF; } }; public abstract LightState toggle(); }
Separate class for each state
// State operations interface public interface LightStateOps { LightState toggle(); } // Light Off implementation public class LightOff implements LightStateOps { public LightState toggle() { return LightState.ON; } } // Light On implementation public class LightOn implements LightStateOps { public LightState toggle() { return LightState.OFF; } }
public enum LightState { OFF(new LightOff()), ON(new LightOn()); private final LightStateOps stateOps; LightState(LightStateOps stateOps) { this.stateOps = stateOps; } public LightState toggle() { return stateOps.toggle(); } }
Sometimes during testing it can be very helpful to get access to private fields.
public class WaterCooker { private Heater heater; public boolean isHot() { return heater.isHot(); } }
@Test public void electricHeaterAssignment() throws Exception { Field heaterField = waterCooker.getClass().getDeclaredField("heater"); heaterField.setAccessible(true); // get old field value Heater oldHeater = (Heater) heaterField.get(waterCooker); // set new field value heaterField.set(waterCooker, new ElectricHeater()); // Assert ... }
Using an enumeration type as Java annotion value makes your code type safe because it can be prechecked by your Java compiler.
@Documented @Inherited @Retention(RUNTIME) @Target({ ElementType.PACKAGE }) public @interface DDDLayer { public enum DDDLayerType { USER_INTERFACE, APPLICATION_LAYER, DOMAIN_LAYER, INFRASTRUCTUR_LAYER; } DDDLayerType value(); }
You can cast the return type of a method to any type by using the following statements. This can be useful if you unmarshal a serialized object.
public <T> T autoCastType(...) throws IOException { @SuppressWarnings("unchecked") T t = (T) methodThatReturnsObject(...); return t; }
In some cases (e.g. type casting) it is necessary to know its generic superclass. The following implementation extracts that information on creation.
public abstract class Processor<T extends AbstractThing> { private final Class<T> genericType; public Processor() { genericType = (Class<T>) ((ParameterizedType) this .getClass().getGenericSuperclass()); getActualTypeArguments()[0]; } }