# Closure vs Action

**URL:** <https://discuss.gradle.org/t/closure-vs-action/37806>\
**Category:** Help/Discuss\
**Created:** [October 5, 2020, 5:03pm UTC](https://discuss.gradle.org/t/closure-vs-action/37806 "2020-10-05T17:03:02Z")\
**Posts on this page:** 10\
**Page:** 1

<div class="post-metadata">

**Author:** ![explorer1680](https://avatars.discourse-cdn.com/v4/letter/e/6de8d8/32.png) [@explorer1680](https://discuss.gradle.org/u/explorer1680)\
**Post date:** [October 5, 2020, 5:03pm UTC](https://discuss.gradle.org/t/closure-vs-action/37806/1 "2020-10-05T17:03:02Z")

</div>

I see in the task doc, there are:

[`Task`](https://docs.gradle.org/current/dsl/org.gradle.api.Task.html)`doLast` ([`Closure`](https://docs.groovy-lang.org/2.5.12/html/gapi/groovy/lang/Closure.html) action)

and

[`Task`](https://docs.gradle.org/current/dsl/org.gradle.api.Task.html)`doLast` ( `Action` \<? super [`Task`](https://docs.gradle.org/current/dsl/org.gradle.api.Task.html)\> action)

What is the difference between Clousre and Action?  
I know how to use Closure to define doLast:

```
task hello {
   doLast {
        println 'Hello Earth'
   }
}

```

how to do it with Action?

Thanks.

---

<div class="post-metadata">

**Author:** ![jjustinic](https://sea1.discourse-cdn.com/gradle/user_avatar/discuss.gradle.org/jjustinic/32/9005_2.png) [@jjustinic](https://discuss.gradle.org/u/jjustinic)\
**Post date:** [October 6, 2020, 2:51am UTC](https://discuss.gradle.org/t/closure-vs-action/37806/2 "2020-10-06T02:51:11Z")

</div>

Much of the Gradle core is written in Java. The API contains an `Action` interface that has its `execute` method called. This is much easier to implement from other JVM languages. The method that takes a `Closure` is just a helper for the Groovy DSL. Behind the scenes, it actually creates a `ClosureBackedAction` and then delegates to the method with the `Action`.

The full, very verbose version could look like this:

```java
project.task("hello", new Action<Task>() {
    @Override
    public void execute(Task task) {
        task.doLast(new Action<Task>() {
            @Override
            public void execute(Task task) {
                System.out.println("Hello Earth");
            }
        });
    }
});

```

However, with Java 8, this can be shortened as a lambda due to `Action` being functional interface:

```java
project.task("hello", task -> {
    task.doLast(task -> {
        System.out.println("Hello Earth");
    });
});

```

or even:

```java
project.task("hello", task -> task.doLast(t -> System.out.println("Hello Earth")));

```

If you’re writing a build script using the Groovy DSL, just use the `Closure`. Otherwise, the `Action` is much easier from Java, especially in 8+, which you need anyway for recent versions of Gradle.

---

<div class="post-metadata">

**Author:** ![Chris\_Dore](https://sea1.discourse-cdn.com/gradle/user_avatar/discuss.gradle.org/chris_dore/32/7592_2.png) [@Chris\_Dore](https://discuss.gradle.org/u/Chris_Dore)\
**Post date:** [October 6, 2020, 3:54am UTC](https://discuss.gradle.org/t/closure-vs-action/37806/3 "2020-10-06T03:54:13Z")

</div>

> [@jjustinic](#):
>
> However, with Java 8, this can be shortened as a lambda due to `Action` being functional interface

Extra note; do not use lambdas for task actions (doFirst/doLast). If lambdas are used Gradle cannot properly track the task implementation for up-to-date/caching purposes.

Buried in [Authoring Tasks](https://docs.gradle.org/current/userguide/more_about_tasks.html#sec:up_to_date_checks)

> For tracking the implementation of tasks, task actions and nested inputs, Gradle uses the class name and an identifier for the classpath which contains the implementation. There are some situations when Gradle is not able to track the implementation precisely:
> 
> Unknown classloader
> 
> When the classloader which loaded the implementation has not been created by Gradle, the classpath cannot be determined.
> 
> Java lambda
> 
> Java lambda classes are created at runtime with a non-deterministic classname. Therefore, the class name does not identify the implementation of the lambda and changes between different Gradle runs.
> 
> When the implementation of a task, task action or a nested input cannot be tracked precisely, Gradle disables any caching for the task. That means that the task will never be up-to-date or loaded from the [build cache](https://docs.gradle.org/current/userguide/build_cache.html#sec:task_output_caching).

---

<div class="post-metadata">

**Author:** ![explorer1680](https://avatars.discourse-cdn.com/v4/letter/e/6de8d8/32.png) [@explorer1680](https://discuss.gradle.org/u/explorer1680)\
**Post date:** [October 6, 2020, 1:35pm UTC](https://discuss.gradle.org/t/closure-vs-action/37806/4 "2020-10-06T13:35:17Z")

</div>

Thanks for @jjustinic and @Chris_Dore, I understand now.

---

<div class="post-metadata">

**Author:** ![gavenkoa](https://sea1.discourse-cdn.com/gradle/user_avatar/discuss.gradle.org/gavenkoa/32/6263_2.png) [@gavenkoa](https://discuss.gradle.org/u/gavenkoa)\
**Post date:** [November 23, 2020, 10:43pm UTC](https://discuss.gradle.org/t/closure-vs-action/37806/5 "2020-11-23T22:43:20Z")

</div>

> The method that takes a `Closure` is just a helper for the Groovy DSL. Behind the scenes, it actually creates a `ClosureBackedAction` and then delegates to the method with the `Action` .

For curious how `ClosureBackedAction` is here read:

```gradle
subprojects/model-core/src/main/java/org/gradle/util/ConfigureUtil.java
subprojects/model-core/src/main/java/org/gradle/internal/instantiation/generator/AsmBackedClassGenerator.java
subprojects/model-core/src/main/java/org/gradle/internal/instantiation/generator/AbstractClassGenerator.java

```

As Gradle transitioned from Groovy `Closure` to `Action`.

Implementations of interfaces with `Closure` jump to `Action` with help of `ConfigureUtil.configure()`.

Seems that new API is built around `Action` only ))

---

<div class="post-metadata">

**Author:** ![gavenkoa](https://sea1.discourse-cdn.com/gradle/user_avatar/discuss.gradle.org/gavenkoa/32/6263_2.png) [@gavenkoa](https://discuss.gradle.org/u/gavenkoa)\
**Post date:** [November 23, 2020, 10:47pm UTC](https://discuss.gradle.org/t/closure-vs-action/37806/6 "2020-11-23T22:47:18Z")

</div>

> [@Chris\_Dore](#):
>
> Extra note; do not use lambdas for task actions (doFirst/doLast). If lambdas are used Gradle cannot properly track the task implementation for up-to-date/caching purposes.

And what we see in built-in `JavaPlugin.java`?

```gradle
project.getTasks().withType(Test.class).configureEach(test -> {
  test.getConventionMapping().map("testClassesDirs", () -> sourceSetOf(pluginConvention, SourceSet.TEST_SOURCE_SET_NAME).getOutput().getClassesDirs());
  test.getConventionMapping().map("classpath", () -> sourceSetOf(pluginConvention, SourceSet.TEST_SOURCE_SET_NAME).getRuntimeClasspath());
  test.getModularity().getInferModulePath().convention(javaPluginExtension.getModularity().getInferModulePath());
});

```

Where is the truth?

---

<div class="post-metadata">

**Author:** ![gavenkoa](https://sea1.discourse-cdn.com/gradle/user_avatar/discuss.gradle.org/gavenkoa/32/6263_2.png) [@gavenkoa](https://discuss.gradle.org/u/gavenkoa)\
**Post date:** [November 23, 2020, 10:56pm UTC](https://discuss.gradle.org/t/closure-vs-action/37806/7 "2020-11-23T22:56:46Z")

</div>

> [@gavenkoa](#):
>
> Where is the truth?

Probably it is OK to use Java lambdas to configure tasks from within plugin, Really, plugin is fixed and comes from Gradle distro or from Maven repository. Highly unlikely it will change.

But adding Java lambdas to `build.gradle` should be disastrous for caching as the Docs states ))

Now the question: how can I add Java lambda in Groovy `build.gradle` ? Or it is the warning about Kotlin?

---

<div class="post-metadata">

**Author:** ![Chris\_Dore](https://sea1.discourse-cdn.com/gradle/user_avatar/discuss.gradle.org/chris_dore/32/7592_2.png) [@Chris\_Dore](https://discuss.gradle.org/u/Chris_Dore)\
**Post date:** [November 24, 2020, 2:49am UTC](https://discuss.gradle.org/t/closure-vs-action/37806/8 "2020-11-24T02:49:49Z")

</div>

> [@gavenkoa](#):
>
> how can I add Java lambda in Groovy `build.gradle` ?

Groovy does not support Java 8 style lambdas, that’s what Groovy’s Closure is for.

> [@gavenkoa](#):
>
> Probably it is OK to use Java lambdas to configure tasks

Yes, using lambdas for configuration is fine. I was referring to task actions, ones added via Task.doFirst or Task.doLast. For example, if you had a plugin that did something like:

```
class MyPlugin implements Plugin<Project> {
    void apply( Project project ) {
        project.getTasks().getByName( "someTask" ).doLast( t -> t.getLogger().lifecycle( "from my plugin" ) );
    }
}

```

---

<div class="post-metadata">

**Author:** ![gavenkoa](https://sea1.discourse-cdn.com/gradle/user_avatar/discuss.gradle.org/gavenkoa/32/6263_2.png) [@gavenkoa](https://discuss.gradle.org/u/gavenkoa)\
**Post date:** [November 24, 2020, 8:59am UTC](https://discuss.gradle.org/t/closure-vs-action/37806/9 "2020-11-24T08:59:20Z")

</div>

> [@Chris\_Dore](#):
>
> I was referring to task actions, ones added via Task.doFirst or Task.doLast.

Now I got it. Thx!

The examples I saw in Gradle plugins itself use Java 8 lambdas during _configuration phase_.

Your example is clearly showcase task actions for which, as docs warns, Java 8 lambdas might break UPDATE checks. So it only effects `doFirst(Action)` / `doLast(Action)` (or more precisely, according to docs: _implementation of a task, task action or a nested input_).

I tried to find explanation of Java behavior when runtime instantiate lambda but cannot find anything relevant (about lambda class name stability between JVM instances).

Also I wonder how it is possible that there is no problem with Groovy closures. They are also dynamic. Even worse, they usually wrapped around by `ClosureBackedAction` to jump from Closure → Action. So devs managed to make “persistent” fingerprint for Groovy closures but unable to make such for Java lambdas. And here is Kotlin SAM adding complexity…

---

<div class="post-metadata">

**Author:** ![Chris\_Dore](https://sea1.discourse-cdn.com/gradle/user_avatar/discuss.gradle.org/chris_dore/32/7592_2.png) [@Chris\_Dore](https://discuss.gradle.org/u/Chris_Dore)\
**Post date:** [November 24, 2020, 7:44pm UTC](https://discuss.gradle.org/t/closure-vs-action/37806/10 "2020-11-24T19:44:06Z")

</div>

> [@gavenkoa](#):
>
> Also I wonder how it is possible that there is no problem with Groovy closures.

Groovy closures are compiled to inner classes, who’s name will not change if the code is not modified/recompiled. lambdas are not [implemented](https://www.infoq.com/articles/Java-8-Lambdas-A-Peek-Under-the-Hood/) that way.

Groovy that will always produce the same output, unless of course you modify the code enough that the generated class names change:

```
Closure x = {
    println "yo"
}
println x.class.simpleName
// __spock_feature_0_0_closure1
// __spock_feature_0_0_closure1

```

JVM instances can produce different output for this Java:

```
Runnable x = () -> System.out.println("yo");
System.out.println(x.getClass().getSimpleName());
// LTest$$Lambda$3/26305445
// LTest$$Lambda$3/21184090

```
