Gradle中的闭包

Gradle是基于Groovy的DSL基础上的构建工具,Gradle中的闭包,其原型上实际上即Groovy中闭包。而在表现形式上,其实,Gradle更多的是以约定和基于约定基础上的配置去展示。但本质上,大多数配置,实际上都对应着闭包以及闭包的具体使用。java

例如,实际Android项目中,咱们常常看到相似以下的所谓配置项:web

allprojects {
    repositories {
        mavenLocal()
        maven {
            url 'http://maven.aliyun.com/nexus/content/groups/public/' } google() jcenter() } configurations.all { resolutionStrategy.cacheChangingModulesFor 1, 'seconds' } } task clean(type: Delete) { delete rootProject.buildDir delete "${rootProject.rootDir}/jenkinsOut" } 复制代码

当咱们在allprojects上按住command键时,发现有以下图所示的提示。bash

出现的提示指的是此配置项所对应的Gradle中原型,能够点击直接进入对应的Gradle API。闭包

/**
     * <p>Configures this project and each of its sub-projects.</p>
     *
     * <p>This method executes the given closure against this project and its sub-projects. The target {@link Project}
     * is passed to the closure as the closure's delegate.</p> * * @param configureClosure The closure to execute. */ void allprojects(Closure configureClosure); 复制代码

咱们发现,咱们经常使用的allprojects配置,实际上真正对应着的,是一个void allprojects(Closure configureClosure)Java方法,而其后{}中的配置,实际上总体是一个Closure类型的参数,在方法说明中,指出这个方法是为当前项目及其子项目执行给定的闭包,目标@Project做为闭包的委托传递给闭包app

因而,到底什么是闭包,闭包具体的运做机制是怎么样的,有必要实际窥探一番。maven

点击Gradle API中的Closure,能够进入对应的Closure类型声明,实际上对应的是Groovy jar包中的class文件声明。函数

package groovy.lang;

import ...

public abstract class Closure<V> extends GroovyObjectSupport implements Cloneable, Runnable, GroovyCallable<V>, Serializable {
复制代码

Closure,翻译过来是闭包,在JS等语言中也存在闭包的概念,可是,不一样语言中,对于闭包的具体描述或实际的应用,不一样语言,可能还有所不一样。工具

先了解一下Groovy闭包的描述:post

闭包,是一个代码块,或能够理解成一个匿名函数,在外部方法调用时,能够将其做为方法的实参传递给方法的形参,并在方法内部回调此匿名函数,且回调此匿名函数时能够传递实参给到匿名函数的内部去接收,并执行此匿名函数。
同时,此代码块或匿名函数也能够赋值给一个变量,使其具备自执行的能力,且最后一行的执行语句做为匿名函数的返回。
复制代码

看着好像不太容易理解,能够具体看几个实际例子。ui

// 1,定义一个闭包,赋值给一个变量,并进行显示的自我调用。
def t = {
    println "Hello Closure" } // 此处也能够写成t.call() t() // 运行后,输出结果为: Hello Closure 复制代码

其中,以变量的方式调用闭包t()t.call()是等价的。

// 2,定义一个闭包,赋值给一个变量,并进行显示的自我调用,并检测其返回值
def t = {
    println "Hello Closure" "ttt" } println "closure return: " + t.call() // 运行后,输出结果为: Hello Closure closure return: ttt 复制代码
// 3,定义一个闭包,赋值给一个变量,并进行显示的自我调用,调用时向闭包传递实参
def t = {
    println "Hello Closure, the param value is: " + it } t("mm") // 运行后,输出结果为: Hello Closure, the param value is: mm 复制代码

调用闭包时,若是向闭包传递实参,闭包内部若是没有声明形参接收,默认是以it的变量的一个形参去接收实参。

所以,例3其实是等价于:

def t = {
    it ->
        println "Hello Closure, the param value is: " + it } t("mm") 复制代码
// 4,若是闭包中显示的声明了形参,则以显示的声明的形参去接收实参
def t = {
    x, y ->
        println "Hello Closure, the param value is: " + x + ", " + y } t("mm", "nn") // 运行后,输出结果为: Hello Closure, the param value is: mm, nn 复制代码

以上,都是将闭包赋值给一个变量后,进行的闭包的调用行为。

同时,咱们也能够将闭包做为一个方法实参,在方法调用时传递给方法形参,而后方法内部造成对此闭包的回调。

// 5,将闭包做为一个方法实参,在方法调用时传递给方法形参,而后方法内部造成对此闭包的回调
class Person {

    String getName(Closure closure) {
        closure("cc", "dd") } } def t = { x, y -> println "Hello Closure, the param value is: " + x + ", " + y } new Person().getName(t) // 运行后,输出结果为: Hello Closure, the param value is: cc, dd 复制代码

例5中的闭包若是没有事先赋值给变量t,而也能够直接使用,效果等价于:

class Person {

    String getName(Closure closure) {
        closure("cc", "dd") } } new Person().getName({ x, y -> println "Hello Closure, the param value is: " + x + ", " + y }) 复制代码

闭包做为方法中的最后一个参数,能够从()中拿出来,则等价于:

new Person().getName(){ x, y -> println "Hello Closure, the param value is: " + x + ", " + y } 复制代码

同时,方法后的()能够去掉,则等价于:

new Person().getName {
    x, y ->
        println "Hello Closure, the param value is: " + x + ", " + y } 复制代码

若是外部调用闭包的方法传递实参时,没有传递实参或只传递了一个参数(若是没有传递实参,则it为null),则进一步演化成:

class Person {

    String getName(Closure closure) {
        closure("cc") } } new Person().getName { println "Hello Closure, the param value is: " + it } 复制代码

这也就是咱们在Gradle中常常见到的闭包形式,即表面上只有{}的配置形式。

将闭包理解成一个特殊的匿名函数,不管是经过变量的自调用,仍是做为方法实参的传递后,在方法内部被回调,闭包的最后一行执行被当作匿名函数的总体返回,均可以很好的得以理解。同时,也能容易的理解闭包能够嵌套使用等(即当作匿名函数的嵌套)。

如:以Gradle中可能常常见到的each写法为例:

dirs.each { dir ->
    java.srcDir("src/$dir/java") res.srcDir("src/$dir/res") } 复制代码

实际上内部对应的执行过程为:

public static <T> List<T> each(List<T> self, @ClosureParams(FirstGenericType.class) Closure closure) {
    return (List)each((Iterable)self, closure); } public static <T> Iterable<T> each(Iterable<T> self, @ClosureParams(FirstGenericType.class) Closure closure) { each(self.iterator(), closure); return self; } public static <T> Iterator<T> each(Iterator<T> self, @ClosureParams(FirstGenericType.class) Closure closure) { while(self.hasNext()) { Object arg = self.next(); closure.call(arg); } return self; } 复制代码

显然,内部最终经过closure.call(arg)回调了闭包自身,并向闭包传递了实参。

闭包在Gradle中的配置中,被大量使用。理解Gradle中的闭包,对一些特殊的写法,如Gradle构建生命周期中的闭包回调中的实参使用等,能够有很好的运用。

如常见的在Gradle构建的配置阶段中的afterEvaluate hook中,能够设置相关task的依赖关系等。此时,it接收的是回传进来的当前project实参。

afterEvaluate {
    ...

    Task assembleJenkinsTask = rootProject.tasks.getByName('assembleJenkins') Task unitTestTask = it.tasks.findByName('testDebugUnitTest') if (unitTestTask != null) { assembleJenkinsTask.dependsOn unitTestTask } ... } 复制代码

在必定意义上,Groovy中闭包的概念,以及其实际的用法上,实质上根Java 8中的lambda表达式具备很相近的含义。

做者:HappyCorn 连接:https://juejin.im/post/5c4af28be51d4511dc72fcca 来源:掘金 著做权归做者全部。商业转载请联系做者得到受权,非商业转载请注明出处。
相关文章
相关标签/搜索