• Gradle中的闭包


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

    例如,实际Android项目中,我们经常看到类似如下的所谓配置项:

    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键时,发现有如下图所示的提示。

    出现的提示指的是此配置项所对应的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作为闭包的委托传递给闭包

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

    点击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闭包的描述:

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

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

    // 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
    来源:掘金
    著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
  • 相关阅读:
    第四周PLECS仿真
    三相异步电动机预习笔记
    第三周PLECS仿真
    《自动化技术中的进给电气传动》 1.3节及《控制系统设计指南》 第一,二章设计指南读书笔记
    第二周 PLECS仿真
    机电传动课程学习
    《实时控制软件设计》2017年度教学总结
    《实时控制软件设计》2017年教学内容
    《机电传动控制》(2017)综合作业
    《机电传动控制》(2017)第十一周作业
  • 原文地址:https://www.cnblogs.com/lwbqqyumidi/p/10322034.html
Copyright © 2020-2023  润新知