前言

最近看到DSL这个货色,不禁的感觉外面能够利用Kotlin的一些个性能简化代码,所以具体来看看它是如何实现的。

注释

首先一上来就说原理或者对于不相熟Kotlin的来说会感觉有点突兀,所以我筹备从头梳理一下。

约定

Kotlin的约定咱们在平时开发中必定用到过,不过咱们没有认真去留神这个名词而已。约定的概念就是:应用与惯例办法调用语法不同的、更简洁的符号,调用着有着非凡命名的函数。

这里提取2个关键点,一个是更简洁的符号调用,一个是非凡命名的函数。说白了就是让函数调用更加简洁。

比方咱们最相熟的集和调用 [index] 来 代替 get(index),咱们本人也来定义个类,来实现一下这个约定:

data class TestBean(val name: String,val age: Int){    //定义非常简单 应用operator重载运算符get办法    operator fun  get(index : Int): Any{        return when(index) {            0 -> name            1 -> age            else -> name        }    }}

而后咱们在应用时:

//这里就能够应用 [] 来替换 get来简化调用办法了val testBean = TestBean("zyh",20)testBean.get(0)testBean[0]

invoke约定

和下面的get约定一样,[] 就是调用 get 办法的更简洁的形式,这里有个invoke约定,它的作用就是让对象像函数一样调用办法,上面间接来个例子:

data class TestBean(val name: String,val age: Int){    //重载定义invoke办法    operator fun invoke() : String{        return "$name - $age"    }}

定义完下面代码后,咱们来进行应用:

val testBean = TestBean("zyh",20)//失常调用testBean.invoke()//约定后的简化调用testBean()

这里会发现testBean对象能够调用invoke办法是失常调用,然而也能够testBean()间接来调用invoke办法,这就是invoke约定的作用,让调用invoke办法更简略。

invoke约定和函数式类型

既然理解了invoke约定,咱们来和lambda联合起来。

咱们晓得函数类型其实就是实现了FunctionN接口的类,而后当函数类型是函数类型时,这时传递给它一个lambda,lambda就会被编译成FunctionN的匿名外部类(当然是非内联的),而后调用lambda就变成了一次FunctionN接口的invoke调用。

还是看个例子代码:

//定义代码class TestInvoke {    //高阶函数类型变量    private var mSingleListener: ((Int) -> Unit)? = null    //设置变量    public fun setSingleListener(listener:((Int) -> Unit)?){        this.mSingleListener = listener    }    //    fun testRun() {        //调用invoke函数        mSingleListener?.invoke(100)        //应用invoke约定,省去invoke        if (mSingleListener != null){            mSingleListener!!(100)        }    }}

定义完下面回调变量后,咱们来应用这个回调,因为咱们晓得高阶函数其实是实现了FunctionN接口的类,也就是实现了:

//留神,这里接口的办法就是invokepublic interface Function1<in P1, out R> : Function<R> {    /** Invokes the function with the specified argument. */    public operator fun invoke(p1: P1): R}

那我也就能够间接应用上面代码来传递参数:

val function1 = object: Function1<Int,Unit> {    override fun invoke(p1: Int) {        Logger.d("$p1")    }}testInvoke.setSingleListener(function1)

这里看起来荒诞不经,因为在testRun函数中咱们调用了invoke函数,把100当做参数,而后这个100会被回调到function1中,然而咱们传递lambda时呢:

val testInvoke  = TestInvoke()testInvoke.setSingleListener { returnInt ->    Logger.d("$returnInt")}

下面代码传递lambda和传递一个类的实例成果是一样的,只不过这里只是一段代码块,没有显示的调用invoke啥的,所以这就是一个个性,当lambda被用作参数被函数调用时,也就能够看成是一次invoke的主动调用

invoke在DSL中的实际:Gradle依赖

这里咱们为什么要说这个invoke依赖呢,很大的起因就是它在一些DSL中有很好的用法,这里咱们就来看个Gradle依赖的应用。

咱们很常见上面代码:

dependencies {    implementation 'androidx.core:core-ktx:1.6.0'    implementation 'androidx.appcompat:appcompat:1.3.1'    //...    }

这里咱们都很司空见惯,感觉这里很像配置项,而不像是代码,其实这个也是一段代码,只不过是这种格调。那这种格调如何实现呢,咱们来简略实现一下:

class DependencyHandler{    //编译库    fun compile(libString: String){        Logger.d("add $libString")    }    //定义invoke办法    operator fun invoke(body: DependencyHandler.() -> Unit){        body()    }}

下面代码写完后,咱们便能够有上面3种调用形式:

val dependency = DependencyHandler()//调用invokedependency.invoke {    compile("androidx.core:core-ktx:1.6.0")}//间接调用dependency.compile("androidx.core:core-ktx:1.6.0")//带接受者lambda形式dependency{    compile("androidx.core:core-ktx:1.6.0")}

由此可见,下面代码第三种形式便是咱们在Gradle配置文件中常见的一种,这里其实就2个关键点,一个是定义invoke函数,一个是定义带接受者的lambda,调用时省去this即可。

总结

其实对于invoke约定和带接受者lambda的写法当初越来越风行了,比方之前的anko库,当初的compose库都是这种申明式的写法,看完原理后,就会发现其实还是很不便的。

后续开始钻研compose的时候,再来补充一波。

相干教程

Android根底系列教程:

Android根底课程U-小结_哔哩哔哩_bilibili

Android根底课程UI-布局_哔哩哔哩_bilibili

Android根底课程UI-控件_哔哩哔哩_bilibili

Android根底课程UI-动画_哔哩哔哩_bilibili

Android根底课程-activity的应用_哔哩哔哩_bilibili

Android根底课程-Fragment应用办法_哔哩哔哩_bilibili

Android根底课程-热修复/热更新技术原理_哔哩哔哩_bilibili

本文转自 https://juejin.cn/post/7047028786969346079,如有侵权,请分割删除。