Skip to main content

What's the deal with Delegates?

Posted by javaerb on January 22, 2004 at 8:21 AM PST

So I've been reading a bit about this battle between Microsoft and Sun ... I mean between C# and Java ... about something I think is called a delegate. The above URL describes them in the context, strangely, of Visual J++. From what it looks like to me, all that C# really has that Java does not is the ability to, effectively, have one method per callback interface. This would be as if, for MouseListener, we instead had MousePressedListener, MouseReleasedListener, etc. We would then say button.addMousePressedListener(this) or something to that effect.
This saves us from the old dilemma: Do I add an inner class that extends MouseAdapter , or do I implement MouseListener with a bunch of empty callback methods? Frankly I see nothing wrong with using an inner class, since I like avoiding exposing the public callback method because it allows it to be called in cases other than event notification. C# poeple, however, seem to want to do the method hiding without the inner class. Sometimes I feel the same way.

I will first address the issue of wanting only a single callback method. Let me start with a simple interface, MouseMotionListener. I first create 2 new interfaces:


public interface MouseMovedListener extends EventListener {
    public void mouseMoved(MouseEvent e);
}

public interface MouseDraggedListener extends EventListener {
    public void mouseDragged(MouseEvent e);
}

Now we need a class to create an interface between them and the original interface:


public class MouseMotionDelegateAdapter implements MouseMotionListener {

    private MouseDraggedListener _mouseDragged;
    private MouseMovedListener _mouseMoved;

    public MouseMotionDelegateAdapter(MouseDraggedListener mouseDragged) {
        _mouseDragged = mouseDragged;
    }

    public MouseMotionDelegateAdapter(MouseMovedListener mouseMoved) {
        _mouseMoved = mouseMoved;
    }

    public void mouseDragged(MouseEvent e) {
        if (_mouseDragged != null) {
            _mouseDragged.mouseDragged(e);
        }
    }

    public void mouseMoved(MouseEvent e) {
        if (_mouseMoved != null) {
            _mouseMoved.mouseMoved(e);
        }
    }
}

Yeah, we could split it up into 2 separate classes, but that wouldn't be cute. (OK, there's one situation in which it would be useful: if a class implements more than one of these new interfaces, a cast would be required in the constructor to disambiguate. But in such a case, probably the traditional listener interface would be more appropriate than these single-method ones.)
Now we use it like this:

public class Example implements MouseMovedListener {

    public Example() {
        JButton button = new JButton();
        button.addMouseMotionListener(new MouseMotionDelegateAdapter(this));
        //  and so forth
    }

    public void mouseMoved(MouseEvent e) {
        // Do something useful.
    }

}

Now, to address the issue of private callbacks. Java's solution is to use inner classes, even if the class has one method that just calls a private method in the containing class. To be more delegate-like, one would want a wrapper class that calls the private methods, based on their signatures. We can easily do this using reflection, but unfortunately Java does not give us compile-time signature checking outside of interfaces. However, since the XXX.class syntax gave us a compile-time replacement for Class.forName("XXX"), why cannot we have compile-time versions of other reflections?

Actually, this is what C# appears to do, and it is just a small bit of syntactic sugar.
It would differ slightly from the class object references, in that it would deal with a signature rather than the Method object itself. Something like:


public CallbackWrapper(method(String, int) privateCallbackMethod) {
}

where method is a keyword and the argument list following it is a signature reference, and the whole thing is treated as a pseudotype. Kind of a combination of how generics work and syntax eerily similar to AspectJ.
OK, maybe it's not so simple. But I bet it's coming -- in Java 1.6 (or, as I am hoping for, 2.0).

Related Topics >>