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:
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:
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.
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.
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.
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 .
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?
Groovy does not support Java 8 style lambdas, that’s what Groovy’s Closure is for.
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" ) );
}
}
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…
Groovy closures are compiled to inner classes, who’s name will not change if the code is not modified/recompiled. lambdas are not implemented that way.
Groovy that will always produce the same output, unless of course you modify the code enough that the generated class names change: