Michal Kordas Michal Kordas - 22 days ago 10
Java Question

Dynamic dispatch without instanceof or getClass() in Java

I have the following domain objects:

public interface Event {}
public class FirstEvent {}
public class SecondEvent {}


Then I have another module, that should be fully decoupled from my domain objects, meaning that it knows domain objects, but domain objects should not know about existence of this additional module.

In this module I receive objects through common interface
Event
and I need to act differently based on specific event type.

Currently my code looks like that:

if (event instanceof FirstEvent.class) {
doFirst();
}
else if (event instanceof SecondEvent.class) {
doSecond();
}


It works fine, but static analysis tools and code reviewers complain that I should not use
instanceof
and I should replace it with more Object-Oriented approach. Reflection or
getClass()
is also not an option.

How to do that in Java?

I've reviewed many existing questions about replacements of
instanceof
but all of them suggested adding some logic straight into domain objects. However, in this case I don't want to pollute them with logic specific to just my module.

Answer

The Visitor pattern, aka Double Dispatch, is generally useful here.

An interface is defined with a method for each known Event type, and each event implements an interface method which allows outside objects to call it with an implementation of that interface. The event then ensures that the type specific method of the interface is called with its own 'this' reference so you don't get any explicit down-casting.

public interface EventVisitor  {
    visit(FirstEvent firstEvent);
    visit(SecondEvent secondEvent);
}

public class FirstEvent {
    ...
    public void allowVisit(EventVisitor ev) {
        ev.visit(this); // calls the 'FirstEvent' overriden method
    }
    ...
}


public class SecondEvent {
    ...
    public void allowVisit(EventVisitor ev) {
        ev.visit(this); // calls the 'SecondEvent' overriden method
    }
    ... 
}

public class MyOtherObject implements EventVisitor, EventListener {
   ...
   public void signalEvent(Event e) {
       e.allowVisit(this);
   }

   public void visit(FirstEvent e) {
       // handle FirstEvent type
   }

   public void visit(SecondEvent e) {
       // handle SecondEvent type
   }

}

The downside of this type of thing is that it becomes difficult to add new Event types because your EventListener interface has to enumerate them. You can 'kind' of get around that with a catch-all method but it's messy and still difficult to upgrade.

Comments