前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Kotlin invoke约定,让Kotlin代码更简洁

Kotlin invoke约定,让Kotlin代码更简洁

原创
作者头像
用户9253515
发布2022-01-05 20:33:59
1K0
发布2022-01-05 20:33:59
举报
文章被收录于专栏:Android开发技术

前言

最近看到DSL这个东西,不由的觉得里面可以利用Kotlin的一些特性能简化代码,所以具体来看看它是如何实现的。

正文

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

约定

Kotlin的约定我们在平时开发中肯定用到过,不过我们没有仔细去注意这个名词而已。约定的概念就是:使用与常规方法调用语法不同的、更简洁的符号,调用着有着特殊命名的函数。

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

比如我们最熟悉的集和调用 [index] 来 替代 get(index),我们自己也来定义个类,来实现一下这个约定:

代码语言:javascript
复制
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
        }
    }

}

然后我们在使用时:

代码语言:javascript
复制
//这里就可以使用 [] 来替换 get来简化调用方法了
val testBean = TestBean("zyh",20)
testBean.get(0)
testBean[0]

invoke约定

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

代码语言:javascript
复制
data class TestBean(val name: String,val age: Int){

    //重载定义invoke方法
    operator fun invoke() : String{
        return "$name - $age"
    }

}

定义完上面代码后,我们来进行使用:

代码语言:javascript
复制
val testBean = TestBean("zyh",20)
//正常调用
testBean.invoke()
//约定后的简化调用
testBean()

这里会发现testBean对象可以调用invoke方法是正常调用,但是也可以testBean()直接来调用invoke方法,这就是invoke约定的作用,让调用invoke方法更简单。

invoke约定和函数式类型

既然了解了invoke约定,我们来和lambda结合起来。

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

还是看个例子代码:

代码语言:javascript
复制
//定义代码
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接口的类,也就是实现了:

代码语言:javascript
复制
//注意,这里接口的方法就是invoke
public interface Function1<in P1, out R> : Function<R> {
    /** Invokes the function with the specified argument. */
    public operator fun invoke(p1: P1): R
}

那我也就可以直接使用下面代码来传递参数:

代码语言:javascript
复制
val function1 = object: Function1<Int,Unit> {
    override fun invoke(p1: Int) {
        Logger.d("$p1")
    }
}
testInvoke.setSingleListener(function1)

这里看起来合情合理,因为在testRun函数中我们调用了invoke函数,把100当做参数,然后这个100会被回调到function1中,但是我们传递lambda时呢:

代码语言:javascript
复制
val testInvoke  = TestInvoke()
testInvoke.setSingleListener { returnInt ->
    Logger.d("$returnInt")
}

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

invoke在DSL中的实践:Gradle依赖

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

我们很常见下面代码:

代码语言:javascript
复制
dependencies {

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

这里我们都很习以为常,感觉这里很像配置项,而不像是代码,其实这个也是一段代码,只不过是这种风格。那这种风格如何实现呢,我们来简单实现一下:

代码语言:javascript
复制
class DependencyHandler{
    //编译库
    fun compile(libString: String){
        Logger.d("add $libString")
    }
    //定义invoke方法
    operator fun invoke(body: DependencyHandler.() -> Unit){
        body()
    }
}

上面代码写完后,我们便可以有下面3种调用方式:

代码语言:javascript
复制
val dependency = DependencyHandler()
//调用invoke
dependency.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,如有侵权,请联系删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • 正文
    • 约定
      • invoke约定
        • invoke约定和函数式类型
          • invoke在DSL中的实践:Gradle依赖
          • 总结
          • 相关教程
          领券
          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档