Skip to content
Branch: master
Go to file

Latest commit


Failed to load latest commit information.
Latest commit message
Commit time

State machines

State machines in Java.

Enables both type checked and runtime checked transitions

Typechecked, if we try to transition straight from green to red it will fail to compile

interface TrafficLight extends State<TrafficLight> {}
static class Green implements TrafficLight, TransitionTo<SolidAmber> {}
static class SolidAmber implements TrafficLight, TransitionTo<Red> {}
static class Red implements TrafficLight, TransitionTo<FlashingAmber> {}
static class FlashingAmber implements TrafficLight, TransitionTo<Green> {}

public void traffic_light_typechecked_example() {
    Green signal = new Green();
    //uncomment a transition and it will fail to compile.
    signal = signal

We can still have typechecked transitions even where multiple state transitions are possible

static class Pending implements OrderStatus, BiTransitionTo<CheckingOut, Cancelled> {}

pending.transition(CheckingOut::new); // fine
pending.transition(Cancelled::new);   // fine
pending.transition(Refunded::new);   // Compile Error

When using the runtime checked transitions we'd throw an exception if we can't transition

public void runtime_transitions_possible() {
   TrafficLight light = new Green();
   light = light

   assertTrue(light instanceof SolidAmber);
@Test(expected = State.InvalidStateTransitionException.class)
public void runtime_transitions_throw_exception_when_not_possible() {
    TrafficLight light = new Green();
    light = light

We can add behaviour on states so that we can perform an appropriate action for the state we're in

public void behaviour_on_states() throws OhNoes {
    Customer customer = customer("");

    OrderStatus state = new Pending();
    state.notifyProgress(customer, emailSender);


    state = state

    state.notifyProgress(customer, emailSender);

    verify(emailSender).sendEmail(, "Your order is on its way");
    verify(emailSender).sendEmail("", "Customer order pending");

interface OrderStatus extends State<OrderStatus> {
    default void notifyProgress(Customer customer, EmailSender sender) {}
static class Purchased implements OrderStatus, BiTransitionTo<Shipped, Failed> {
    public void notifyProgress(Customer customer, EmailSender emailSender) {
        emailSender.sendEmail("", "Customer order pending");
        emailSender.sendEmail(, "Your order is on its way");
static class Cancelled implements OrderStatus {
    public void notifyProgress(Customer customer, EmailSender emailSender) {
        emailSender.sendEmail("", "Customer order cancelled");
        emailSender.sendEmail(, "Your order has been cancelled");

We can also have guards before and after transitions. Our states can be non-static classes if we want to access state in the enclosing class, like this logger.

Logger failureLog = Logger.getLogger("failures");
class Failed implements OrderStatus {
    public void afterTransition(OrderStatus from) {
        failureLog.warning("Oh bother! failed from " + from.getClass().getSimpleName());


Typechecked or Runtimechecked State Machines in Java



No releases published


You can’t perform that action at this time.