Polymorphically Dispatching in Java
Asked Answered
W

5

16

In the following, I want EventHandler to handle EventA one way, EventB another way, and any other Events (EventC, EventD) yet another way. EventReceiver receives only a reference to an Event and calls EventHandler.handle(). The version that always gets called, of course, is EventHandler.handle(Event event).

Without using instanceOf, is there a way to polymorphically dispatch (perhaps via another method in EventHandler or generics) to the appropriate handle method?

class EventA extends Event {
}

class EventB extends Event {
}

class EventC extends Event {
}

class EventD extends Event {
}

class EventHandler {
    void handle(EventA event) {
       System.out.println("Handling EventA");
    }

    void handle(EventB event) {
       System.out.println("Handling EventB");
    }

    void handle(Event event) {
       System.out.println("Handling Event");
    }
}

class EventReceiver {
    private EventHandler handler;

    void receive(Event event) {
        handler.handle(event);
    }
}    
Wrinkly answered 19/1, 2011 at 15:50 Comment(0)
D
14

Sounds like a case for applying (a variant of) the Visitor pattern. (In mainstream OO languages such as C++, C# and Java, methods are single dispatch, i.e. can only be polymorphic on one type at a time. Visitor allows one to implement double dispatch.)

This however requires that you be able to modify the Event classes as well, and creates a dependency from Events to (a base interface of) EventHandler.

class EventA extends Event {
  public handleBy(EventHandler eh) {
    eh.handleEventA(this);
  }
}

class EventB extends Event {
  public handleBy(EventHandler eh) {
    eh.handleEventB(this);
  }
}

class EventHandler {
    void handleEventA(EventA event) {
       System.out.println("Handling EventA");
    }

    void handleEventB(EventB event) {
       System.out.println("Handling EventB");
    }

    void handle(Event event) {
       event.handleBy(this);
    }
}
Dante answered 19/1, 2011 at 15:52 Comment(2)
I thought about the Visitor pattern and the acyclic Vistor pattern. Unfortunately, I can't modify the Event class. Moreover, I'll need to add many specialized Events over time.Wrinkly
Well, I guess since Java supports only single-dispatch, multiple-dispatch can only be kluged/mocked, as is done here and in the Visitor Wikipedia article. Also, I think the double-dispatch consists of 1) dispatching based on argument (kluged by function overloading in Java), and 2) standard polymorphism on the object the method is invoked...but in this case, eh is not polymorphic, so really we only have the 1) type dispatch here.Sketchy
B
2

This is a use case for double-dispatch, no (which as one may indeed know is either called Visitor) ? I'll implement your example for EventA only

class Event {
    /**
     * Will do some type escalation
     */
    void handleWith(EventHandler care) {
        care.handle(this);
    }
}



class EventA extends Event {
    /**
     * As event is EventA, this implementation is called, with its correct type forced by the cast
     */
    void handleWith(EventHandler care) {
        care.handle((EventA) this);
    }
}

class EventHandler {
    /**
     * Finally comes here
     */
    void handle(EventA event) {
       System.out.println("Handling EventA");
    }

    void handle(EventB event) {
       System.out.println("Handling EventB");
    }

    void handle(Event event) {
       System.out.println("Handling Event");
    }

    /**
     * Go here first and dispatch call to Event class
     */
    void doHandle(Event event) {
        event.handleWith(this);
    }
}

class EventReceiver {
    private EventHandler handler;

    void receive(Event event) {
        handler.doHandle(event);
    }
}    
Bouncing answered 19/1, 2011 at 15:56 Comment(0)
Y
2

Java only has polymorphic dispatch on the object a method is invoked on. That means, the only way to get real polymorphism is to put the handle() method into the Event interface itself. I'd actually say that is the overall better and more OO solution, since a "handler" that operates on data objects is rather procedural.

Any other solution (like a map of handler objects keyed on the class) is going to be more complex and less flexible, especially concerning inheritance.

Yseult answered 19/1, 2011 at 15:58 Comment(1)
I agree Michael. The Events, however, can be used in different context and will need to do things differently based on the context.Wrinkly
E
0

You could use a Map and map event types to event handlers.

Map<Class<? extends Event>, Handler> map =
    new HashMap<Class<? extends Event>, Handler>();

void receive(Event event) {
    Handler handler = map.get(event.getClass());
    handler.handle(event);
}
Edgeways answered 19/1, 2011 at 15:55 Comment(0)
O
0

I know how you can do it with some pre-processing. Use something like this:

public abstract class EventHandler<T extends Event> {
   public abstract void handle(T event, Class<T> c);
   public abstract Class<T> handles();
}

public class EventHandlerA extends EventHandler<EventA> {
   @Override
   public void handle(EventA event, Class<EventA> c) {
      System.out.println(event);
   }

   @Override
   public Class<EventA> handles() {
      return EventA.class;
   }    
}

Then use a map to organize your handlers

HashMap<Class<?>,Collection<EventHandler<?>> handlers;

When an event needs to be handled just retrieve the handlers from the map. If Class.equals() and Class.hashCode() doesn't work how you want then you'll need a wrapper to get the behavior you want.

Opera answered 17/12, 2012 at 19:34 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.