kotlin小知识系列1-dsl和invoke约定

陌路风尘2018-12-10 08:18:55

kotlin小知识系列1-dsl和invoke约定

kotlin小知识系列是连载文,会不定期的进行更新

invoke约定

kotlin中,为了更加方便的支持dsl,提供了一个特殊的函数,invoke约定函数。看下代码说明它的作用。

class Dependencies {    fun compile(str: String) {
        println("compile $str")
    }    fun annotationProcessor(str: String) {
        println("annotationProcessor $str")
    }    operator fun invoke(block: Dependencies.() -> Unit) {
        block()
    }
}fun main(args: Array<String>) {
    val dependencies=Dependencies()
    dependencies {
        compile("com.android.support:support-v4:23.1.1")
        annotationProcessor("com.jakewharton:butterknife-compiler:8.4.0")
    }    //上面的调用方式相当于下面
    dependencies.compile(“com.android.support:support-v4:23.1.1”)
    dependencies.annotationProcessor("com.jakewharton:butterknife-compiler:8.4.0")
}

通过代码可以看出invoke约定函数,就是为对象提供了一个直接调用方法的途径。当然了,除了上述类似dsl的调用途径外,invoke本身也是个普通方法。也可以对其进行参数设置。例如:

class DownloadManager {    fun download(url: String) {
        println("downManager down $url ")
    }    operator fun invoke(url: String) {
        println("invoke  $url ")
        download(url)
    }
}

 val downloadManager = DownloadManager()    //正常调用方法
    downloadManager.download("www.demon-yu.com/file")    //高级调用方法
    downloadManager("www.demon-yu.com/file1")
    downloadManager("www.demon-yu.com/file2")
    downloadManager("www.demon-yu.com/file3")

两个例子可是说是比较常用的例子了~

dsl领域专用语言

kotlin本身语言的特性,天生支持dsl。主要用涉及扩展函数、lambda、中缀调用、invoke 约定和函数小括号省略等几个特性。 android的编译环境gradle就是典型的dsl的应用。只是目前gradle使用的是grovvy语言,下面我用kotlin实现一个说明的例子。

class Gradle {    //采用成员变量进行dsl
    val dependencies = Dependencies()    //采用函数dsl,Android不包含invoke函数
    private val _android = Android()    fun android(block: Android.() -> Unit) {
        _android.block()
    }    //invoke约束杉树
    operator fun invoke(block: Gradle.() -> Unit) {
        block()
    }
}//android对象配置class Android {    //两种方式
    var compileSdkVersion = -1
    fun compileSdkVersion(sdk: Int) {
        compileSdkVersion = sdk
    }    //直接赋值
    var targetSdkVersion = 12
    //defaultConfig对象配置
    val defaultConfig = DefaultConfig()

}//android 默认configclass DefaultConfig {    //设置applicationId
    var applicationId = ""
        get() {
            println("get applicationId")            return field
        }
        set(value) {
            println("set applicationId")
            field = value
        }    operator fun invoke(block: DefaultConfig.() -> Unit) {
        block()
    }
}//依赖管理对象class Dependencies {    //编译依赖
    fun compile(str: String) {
        println("compile $str")
    }    //竹节处理器
    fun annotationProcessor(str: String) {
        println("annotationProcessor $str")
    }    operator fun invoke(block: Dependencies.() -> Unit) {
        block()
    }
}fun main(args: Array<String>) {
    var gradle = Gradle()
    gradle {
        android {
            compileSdkVersion(2)
            compileSdkVersion = 2
            targetSdkVersion = 27
            defaultConfig {
                applicationId = "com.google.android.internal"
            }
        }
        dependencies {
            compile("com.android.support:support-v4:23.1.1")
            annotationProcessor("com.jakewharton:butterknife-compiler:8.4.0")
        }
    }
}

下面,我着重分析下几个要点

kotlin的lamada表达式
...class Dependencies {    //编译依赖
    fun compile(str: String) {
        println("compile $str")
    }    //竹节处理器
    fun annotationProcessor(str: String) {
        println("annotationProcessor $str")
    }    operator fun invoke(block: Dependencies.() -> Unit) {
        block()
    }
}
...
   dependencies {
        compile("com.android.support:support-v4:23.1.1")
        annotationProcessor("com.jakewharton:butterknife-compiler:8.4.0")
    }
...

Dependencies 的约定invoke函数的参数是一个带有接收者的lambda表达式,同时由于invoke函数唯一参数是个lambda表达式,可以省略小括号。 所谓带有接收者的lambda表达式,我觉得更像一个闭包环境,而环境的context就是lambda的调用者,如果没有显示的指明调用者的话,就是当前的对象this了。

再看一个显示指明lambda调用者的情况

class Theme {
    var icon: String? = null
    var color: String? = null

    override fun toString(): String {        return "$icon $color"
    }
}fun global(block: Theme.() -> Unit): Theme {
    val theme = Theme()
    theme.block()    return theme
}fun main(args:Array<String>){
    val theme = global {
        icon = "柯南皮肤"
        color = "蓝色"
    }
    println(theme)
}

代码很简单,就不仔细说明了~~


Copyright © 温县电话机虚拟社区@2017