views:

705

answers:

3

What is the Java equivalent of Cocoa delegates?

(I understand that I can have an interface passed to a class, and have that class call the appropriate methods, but I'm wondering if there is any other way to achieve something closer to Cocoa/Objective-C's informal protocols)

+2  A: 

There's nothing stopping you from using the delegate pattern in your Java objects (It's just not a commonly used pattern in the JDK like it is in Cocoa). Just have a delegate ivar of a type that conforms to your WhateverDelegate interface, then in your instance methods that you want delegated, forward the method call onto the delegate object if it exists. You would probably end up with something that looks a lot like this, except in Java instead of Obj-C.

As far as optional interfaces go, that would be more difficult. I would suggest declaring the interface, declaring an abstract class that implements optional methods as empty methods, and then subclassing the abstract class, overriding the optional methods that you want this particular object to implement. There's a potentially severe limitation here due to the lack of multiple inheritance in Java, but that's as close as I could come up with.

Rich Catalano
Throwing `UnsupportedOperationException` is a terrible idea! APIs should never force users to deal with exceptions in normal usage patterns, only for exceptional flow. The delegate pattern in Cocoa is robust in that it silently passes over unimplemented delegate methods.
Quinn Taylor
Very good point. I had forgotten how annoying Java exceptions are.
Rich Catalano
@Quinn Taylor: however, that is how "optional" protocols *have been* done in many parts of the Java library. For example, the "Collection" interface specifies that methods such as add() and remove() are "optional operations", which throw UnsupportedOperationException if they do not support it.
newacct
I don't dispute that many APIs in Java do this, but it doesn't mean it's the best approach. (One the API is public and exceptions are documented, it's quite hard to change.) In "Effective Java" (2nd ed.) page 242, Josh Bloch writes (in bold): **"A well-designed API must not force it's clients to use exceptions for ordinary control flow."** This is part of Item 57, "Use exceptions only for exceptional conditions". New APIs should be well behaved. For example, java.awt.event.MouseAdapter doesn't throw any exceptions, and delegates in particular should not, **especially** for optional methods.
Quinn Taylor
A: 

Best analog to an informal protocol I can think of is an interface that also has an adapter class to allow implementers to avoid implementing every method.

public class MyClass {

    private MyClassDelegate delegate;

    public MyClass () {

    }

    // do interesting stuff

    void setDelegate(MyClassDelegate delegate) {
        this.delegate = delegate;
    }

    interface MyClassDelegate {
        void aboutToDoSomethingAwesome();
        void didSomethingAwesome();
    }

    class MyClassDelegateAdapter implements MyClassDelegate {

        @Override
        public void aboutToDoSomethingAwesome() {
            /* do nothing */
        }

        @Override
        public void didSomethingAwesome() {
            /* do nothing */
        }
    }
}

Then someone can come along and just implement the stuff they care about:

class AwesomeDelegate extends MyClassDelegateAdapter {

    @Override
    public void didSomethingAwesome() {
        System.out.println("Yeah!");
    }
}

Either that, or pure reflection calling "known" methods. But that's insane.

Nick Veys
But as always, the adaptor approach artificially constrains inheritance. Java doesn't have optional interface methods like Obj-C does in protocols, so you won't get something exactly the same. The cleanest (yet still annoying) approach is to implement empty methods.
Quinn Taylor
+2  A: 

The short answer is there's nothing in Java as close as you'd like, but there are alternatives. The delegate pattern isn't hard to implement, it's just not quite as convenient as doing it with Objective-C.

The reason "informal protocols" work in Objective-C is because the language supports categories, which allow you to add methods to existing classes without subclassing them, or even having access to the source code. Thus, most informal protocols are a category on NSObject. This is clearly impossible in Java.

Objective-C 2.0 opts for @optional protocol methods, which is a much cleaner abstraction and preferred for new code, but even further from having an equivalent in Java.

Honestly, the most flexible approach is to define a delegate protocol, then have classes implement all the methods. (With modern IDEs like Eclipse, this is trivial.) Many Java interfaces have an accompanying adapter class, and this is a common approach for not requiring the user to implement a lot of empty methods, but it restricts inheritance, which makes code design inflexible. (Josh Bloch addresses this in his book "Effective Java".) My suggestion would be to only provide an interface first, then add an adapter if it's truly necessary.

Whatever you do, avoid throwing an UnsupportedOperationException for "unimplemented" methods. This forces the delegating class to handle exceptions for methods that should be optional. The correct approach is to implement a method that does nothing, returns a default value, etc. These values should be well documented for methods that don't have a void return type.

Quinn Taylor