Tuesday, October 23, 2007

Feature request for JDK7: delegation

Some while back I wrote about delegation for Java. Delegation? By this I mean direct language support for constructor-based dependency injection of interface implementation, a.k.a. mixins.

Many interesting proposals for JDK7 are in the air: the smell of change is replacing the mustiness of an aging language and I want my chance to improve Java while there is a open window of opportunity.

About delegation

For more details follow my three-year old posting. In a nutshell, delegation is assignment to this in a constructor to inject an interface implementation without the need for manually coding boilerplate forwarding methods.

An example makes this more clear:

interface Blogger {
    boolean post(final String entry);
}

// Without delegation
class MessyCoder implements Blogger {
    private final Blogger bloggerDelegate;

    MessageCodeBlogger(Blogger bloggerDelegate) {
        this.bloggerDelegate = bloggerDelegate;
    }

    public boolean post(final String entry) {
        return bloggerDelegate.post(entry);
    }
}

// With delegation
class CleanCoder implements Blogger {
    CleanCoder(Blogger bloggerDelegate) {
        this = bloggerDelegate;
    }
}

In an example this small, the gain in clarity and bug-avoidance is limited, but for larger interfaces or for multiple interfaces, the gain is proportionately larger.

The bigger picture

Providing cleaner code is only a small benefit of delegates. A larger benefit is that of mixins: dynamic implementation inheritance without breaking the single-inheritance contract of Java. Extension by composition—rather than inheritance—becomes a first-class syntax citizen of the language.

Delegates solve the mixin problem raised by Bruce Eckels about Java generics, and do so without needing aspects. (Although, behind the scenes, the compiler very well may use aspects to implement delegates. But you the over-committed programmer need not spend your short time on this point.)

Of course, you could always just fake it or do it the hard way.

UPDATE: More thoughts here and another solution.

15 comments:

Bob said...

I'd love this.

Talden said...

I'd be tempted to just bite the bullet and add a new keyword, syntax and semantics.

Possibly little more than...

public class X implements Y {
private delegate Y yDelegate = new DefaultY();
}

* The compiler would fabricate the delegating methods.
* Reflection should indicate that a method is just delegation.
* Because creating a delegate uses the usual constructor model, binding between owner and delegate is easily added as constructor arguments.
* Additional semantics are needed to specify all delegation methods are final (unless we consider this the only useful case).
* The delegate field itself should almost certainly be implicitly final.

It would have to be a compile-time error to declare delegates whose interfaces have method signatures that overlap or that overlap with locally declared method signatures.

Of course I've never looked into this issue before so I've almost certainly missed pitfalls in the approach. I'd love to know what they are...

Brian Oxley said...

I find assignment to this perfectly adequate. There is no need to declare a delegate field that would never be used -- the forwarding methods which would refer to that field are synthetic and do not appear in the text of the class.

Talden said...

The only argument I have against the use of 'this =' is that the Delegate might itself implement a number of interfaces by necessity of its own implementation that are also implemented on the Delegator. The Delegator won't necessary want to delegate those to that particular delegate.

In my example it is the declared type of the delegate that defines the delegation.

Consider a List implementation which delegates to ArrayList but wants to make the default Iteration order reversed. We don't want to delegate Iterable to ArrayList in that case.

NB The List example is only to illustrate a single occurrance of interface leakage in delegation - there may be other perfectly good reasons why delegating to ArrayList here would be bad.

Sam Halliday said...

The delegate keyword looks great! I'm all for it. Combine this with "properties" and it'd cut down on most of the boilerplate in Java. Cut out handling null in equals, and it'd almost all go ;-)

The keyword *is* needed... what if the delegate is editing the behaviour of one of the methods? Perhaps to do some logging. You can't do that with the original code.

Brian Oxley said...

Talden, if the delegate implements more than the original class implements, only what the original class implements is visible -- the delegate does not add a new contract to the original class, it only implements what is required and no more. Does this answer your point?

Brian Oxley said...

Sam, are you suggesting something like this?

class Scout implements Camper {
public void camp() { ... }
}

class BoyScout implements Camper {
BoyScout(Scout scout) {
this = scout;
}

public void camp() {
callHomeFirst();
??? // How to forward to delegate?
}
}

Sam Halliday said...

@blinkey

Yeah... that's exactly the scenario. With the new keyword, I was thinking that the variable declared as the delegate could be directly accessed, and that any explicit declaration of a method would have precedence... so

public class X implements Y {
private delegate Y yDelegate = new DefaultY();

public void y(){
logAction();
yDelegate.y();
}
}

Sam Halliday said...

Of course, it would also be possible to do this with annotations instead of a keyword. Consider

public class X implements Y {
@Delegate(Y.class)
private Y yDelegate;
}

and then the compiler could construct the missing methods and dependency injection could potentially be used for assigning yDelegate.

Talden said...

@Binkley ...the delegate does not add a new contract to the original class...

I don't think that quite covers it - the delegate and the delegator might implement the same interface that is not wanted in the delegation model.

class C implements List, Iterable {
C() {
this = new ArrayList();
}

public Iterator iterator() {
// ... my special iteration rule...
}
}

How does the 'this =' model express that only List should be delegated? This could, I suppose, be resolved with casting or another declaration

// Most wouldn't expect this cast to be meaningful.
this = (List) new ArrayList();

or

// Just as verbose as providing the keyword.
List list = new ArrayList();
this = list;


@sam ...Annotations...

At first glance it seems that accessibility modifiers, final and synchronised could all be replaced by annotations too (once the 'annotations everywhere' JSR makes its way into circulation) - I doubt the resulting code would be very readable.
I would hope that the annotation syntax is not the preferred direction for what are, to me at least, fundamental language constructs. Annotations should primarily be about toolsets not about the first consumer of the code - the compiler.


There are other details I noted when thinking about this a little more.
* To support partial automated delegation, do not try to generate delegation methods for signatures that are locally declared - allowing an implementor to use the shorthand for the boilerplate and only write implementations for the few interesting cases.
* To support delegation in the constructor rules will be needed about when delegates are assigned or a super-constructor or non-delegated method could produce side-effects that trigger delegated behaviour before the delegator is assigned.

Sam Halliday said...

@talden I like the delegate keyword in this instance, but you gotta admit the annotation way does have some advantages... backwards compatibility for one. Adding keywords (remember when 'assert' was added?) to the language introduces backward incompatibilities that are easy to fix... but incompatibilities at the end of the day.

That said, in order for a 'delegate' keyword to break existing code... somebody would have had to have created a type called delegate with a lowercase d, and that's against the naming conventions.

The annotation technique allows for additional parameters too... for example, declaring which interface(s) are to be delegated.

Sam Halliday said...

@talden... mistake in my last comment, of course a 'delegate' keyword would break variables named delegate too. Which is pretty messy. That's certainly the variable name I assign to delegate fields!

I'm not in the same camp with you on annotations... I think they make Java more terse and more readable. They also lend themselves to better documented code as annotations can of course have javadocs... imagine if '@Final, @Volatile' were used from the start. In an IDE, you'd see the full effect of these to the likes of variable visibility across threads. Most people don't know what 'final' or 'volatile' actually do, believing them to be legacy keywords. In this multicore world, they are more relevant than ever! (I'm not debating that we should change these keywords to annotations... it's just an example to show the advantages)

I'd like to see annotations reduce boilerplate in several areas... this post is a prime example. Others are fields with default getter/setter (properties are trying to deal with this), more concise equals and hashCode. They can already be used for dependency injection (check out Guice).

Brian Oxley said...

The ultimate stake-in-the-heart for a new keyword, delegate, is the masses of code it would break which use delegate as a field or variable name.

Talden said...

I'm not sure we should compare introducing 'assert' to introducing 'delegate'.

The 'assert' identifier was used in a very large proportion of projects and, by the nature of the assertion concept was distributed throughout the code-base. In addition the affected projects resided in every corner of the java development.

The 'delegate' keyword would not clash with existing identifiers in nearly as many projects, nor would it be distributed throughout a code-base so completely. Even more importantly, it's far less likely to appear in the vast majority of little utility libraries that were affected by assert so projects won't have to wait for their dependencies to release updates.

That said, adding it as a keyword does steal it away from developers as a useful identifier. Making it a keyword could well force the use of less relevant identifiers.

I wouldn't want to see a slew of new keywords, but I think that finding a clear and capable syntax for delegation will reduce such a swathe of boilerplate that it warrants consideration. Like any language change, it needs to be clear and it's benefit needs to outweigh the pain of adopting it - I feel that first-class delegation might fall into this category as might a clear property notation (not necessarily that keyword).

Brian said...

I posted some comments on this over on my blog (tried to post them here, but blogger seemed to temporarily not recognize me): http://www.briangoetz.com/blog/?p=43