4

What should be the preferable Java interface or similar pattern that could be used as a generic callback mechanism?

For example it could be something like

public interface GenericCallback
{
    public String getID();
    public void callback(Object notification);
    // or public void callback(String id, Object notification);
}

The ID would be needed for cases of overriden hashCode() methods so that the callee identifies the caller.

A pattern like the above is useful for objects that needs to report back to the class they were spawned from a condition (e.g., end of processing).

In this scenario, the "parent" class would use the getID() method of each of these GenericCallback objects to keep a track of them in a Map<String, GenericCallable> and add or remove them according to the notification received.

Also, how should such an interface be actually named?

Many people seem to prefer the Java Observer pattern, but the Observable class defined there is not convenient, since it not an interface to circumvent single inheritance and it carries more functionality than actually needed in the above, simple scenario.

PNS
  • 19,295
  • 32
  • 96
  • 143
  • 4
    Erm, how about `Observer` http://docs.oracle.com/javase/7/docs/api/java/util/Observer.html and `Observable` http://docs.oracle.com/javase/7/docs/api/java/util/Observable.html – Brian Roach Mar 07 '13 at 00:00
  • "The ID would be needed for cases of overriden hashCode() methods so that the callee identifies the caller." I don't get this part – newacct Mar 07 '13 at 00:41
  • If you have an "observable" object with a hashCode() method that changes its return value based on the object state, and you keep track of all the observable objects in a HashMap (e.g., to be able to assert when all these objects have finished executing), then you cannot match any tracked observable. – PNS Mar 07 '13 at 00:52
  • It's better to use specific, meaningful types rather than general, meaningless types. – Tom Hawtin - tackline Mar 07 '13 at 01:42

4 Answers4

24

I would genericize the callback, based upon the type of Object passed. This is particularly useful for EventListeners listening for different classes of events. e.g.

public interface Callback<T> {
   public void callback(T t);
}

You may be able to use the type T as the key in a Map. Of course, if you want to differentiate between two callbacks that take the same argument, like a String, then you'd need something like your getID().

Here my old blog about using this for Event Listeners The interface Events.Listener corresponds to Callback<T> above. And Broadcasters uses a Map to keep track of multiple listeners based upon the class they accept as the argument.

user949300
  • 15,364
  • 7
  • 35
  • 66
4

I'd recommend using Observer pattern since the Observer pattern is the gold standard in decoupling - the separation of objects that depend on each other.

But I'd recommend avoiding using the Java.util.Observable class if you are looking for a generic callback mechanism. Because Observable has a couple of weaknesses: it's not an interface, and forces you to use Object to represent events.

You can define your own event listener like this:

public class MyEvent extends EventObject {
    public MyEvent(Object source) {
        super(source);
    }
}

public interface MyEventListener {
    void handleEvent(EventObject event);
}

public class MyEventSource {
    private final List<MyEventListener> listeners;
    public MyEventSource() {
        listeners = new CopyOnWriteArrayList<MyEventListener>();
    }
    public void addMyEventListener(MyEventListener listener) {
        listeners.add(listener);
    }
    public void removeMyEventListener(MyEventListener listener) {
        listeners.remove(listener);
    }
    void fireEvent() {
        MyEvent event = new MyEvent(this);
        for (MyEventListener listener : listeners) {
            listener.handleEvent(event);
        }
    }
}
Tom Hawtin - tackline
  • 145,806
  • 30
  • 211
  • 305
Jeff
  • 98
  • 4
  • I second what you say about Observable and creating a simpler version of that class as an interface (an approached suggested, for example, at http://stackoverflow.com/questions/11404034/java-interface-observable) also renders the Java Observer interface unusable, since that depends on Observable, too. – PNS Mar 07 '13 at 01:33
  • I'd make a copy of `listeners` before trying to execute callback methods. – Tom Hawtin - tackline Mar 07 '13 at 01:40
  • Even better than making a copy each time, use a CopyOnWriteArayList to hold the listeners. – user949300 Mar 07 '13 at 04:22
1

looks like you want to implement the Observer pattern. In this url is a complete implementation for the observer pattern in Java. In your case the observer will be the callback.

Also If you need to implement something more complex, you will end up doing an event/notifier pattern. Take a look at this other pattern here.

Thanks, @leo.

lemil77
  • 341
  • 1
  • 8
  • The problem with the Observer pattern is that the Observable definition is a class, so due to single inheritance all classes that already subclass another class cannot become observable. – PNS Mar 07 '13 at 00:53
0

Callbacks in Java8 can now be done with the java.util.function package.

See Java 8 lambda Void argument for more information.

Craigo
  • 3,384
  • 30
  • 22