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

/ kotlin / 没有评论 / 110浏览

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 默认config
class 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)
}

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