groovy-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From Cédric Champeau <>
Subject Re: Automatic closure coercion and delegate
Date Tue, 03 May 2016 06:26:27 GMT
2016-05-02 18:11 GMT+02:00 Jochen Theodorou <>:

> On 02.05.2016 16:44, Cédric Champeau wrote:
> [...]
>> Of course, it may look a bit superficial but it is super important for
>> nice DSLs like in Gradle.
> could you give an example of a more complex closure usage?

Sure, basically you can have that kind of configuration:

dependencies {
   compile '...'

(the method is dependencies(Action<? super DependencyContainer>, and we
generate a Closure version at runtime) or:

repositories { // Action<? super RepositoryHander
   maven { Action<? super MavenRepository>
        url '....'

> We should be also aware that this change may break code, since it is
> semantic change and a local method of the same name will no longer be
> called if it exists on the delegate. A functional interface is after all
> not something that came really to exist with java8 only, Callable and
> Runnable are examples that existed before and work with Closure already.
> Implementation wise to have something like
> void configure(Action<Person> config) {
>> config.execute(person)
>> }
> working we need to set the delegate to person, but we don´t know person
> before the method invocation. This means the proxy for the Action
> delegating to the closure config represents must also make the delegation
> setter call.
> And there is also the problem of what we do if Action contains default
> methods - I do not consider our current solution as appropriate anymore.
> But of course that is not essential for the idea at hand.
I have a patch that does the work for functional interfaces already:

public static Object coerceToSAM(Closure argument, Method method,
Class clazz, boolean isInterface) {
    if (argument!=null && clazz.isAssignableFrom(argument.getClass())) {
        return argument;
    if (isInterface) {
        if (Traits.isTrait(clazz)) {
            Map<String,Closure> impl = Collections.singletonMap(
        return Proxy.newProxyInstance(
                new Class[]{clazz},
                new SAMClosure(argument));
    } else {
        Map<String, Object> m = new HashMap<String,Object>();
        m.put(method.getName(), argument);
        return ProxyGenerator.INSTANCE.
                instantiateAggregateFromBaseClass(m, clazz);

private static class SAMClosure extends ConvertedClosure {
    public SAMClosure(final Closure closure) {

    public Object invokeCustom(final Object proxy, final Method
method, final Object[] args) throws Throwable {
        if (args!=null && args.length>0) {
            Closure delegate = (Closure) getDelegate();
        return super.invokeCustom(proxy, method, args);

Doing the same for abstract classes should be straightforward. For static
compilation, it's going to be more complicated and probably requires
transparently invoking a configurer (like Gradle does).

> bye Jochen

View raw message