kotlin扩展函数(一)

kotlin 用了这么久也一直没有整理过,当想整理的时候又不知道又不知道该整理点啥,于是就从项目中用到最多的整理记录一下.项目中用到的扩展函数比较多,所以先从扩展函数开始,这其中包括Kotlin自带的扩展函数和自己写的扩展函数.

什么是扩展函数?

对于我这样一个Kotlin的初学的菜鸡来说经常会为写不出一些优雅的代码而伤神,经常流连于众大佬的gayhub,感叹大佬用的各种新奇的函数...不管三七二十一先copy过来再说,后来知道这些都是一些扩展函数,是 Kotlin 用来简化一些代码的书写产生的.

在Kotlin中的源码标准库(Standard.kt)中提供了一些Kotlin扩展的内置函数可以优化kotlin的编码。Standard.kt是Kotlin库的一部分,它定义了一些基本函数。 这个源代码文件虽然一共不到50行代码,但是这些函数功能都非常强大,像 let、with、run、apply、also 等函数都在其中.

let

在函数块内可以通过 it 指代该对象。返回值为函数块的最后一行或指定return表达式

inline fun <T, R> T.let(block: (T) -> R): R
  1. 在函数体内使用it替代object对象去访问其公有的属性和方法
object?.let{
    it.todo()
...
}

使用场景

  • 一般写法
fun test() {
   val array = intArrayOf(1, 2, 3, 4, 5)
   array.forEach { println(it) }
   println(array.size)
}
  • let 写法
fun test() {
   val array = intArrayOf(1, 2, 3, 4, 5)
   val result = array.let {
   array.forEach { println(it) }
   it.size
   }
   println(result)
}

输出 1 2 3 4 5 5

  1. 当object不为null的条件下,才会去执行let函数体
object?.let{
    it.todo()
}

使用场景

  • 一般写法
newsBean?.listed =1
newsBean?.isNotFirst = false
newsBean?.area ="ShangHai"
  • let写法
newsBean?.let {
   it.listed =1
   it.isNotFirst = false
   it.area ="ShangHai"
}

also

also函数let很像唯一的区别就是返回值的不一样,let是以闭包的形式返回,返回函数体内最后一行的值,如果最后一行为空就返回一个Unit类型的默认值。而also函数返回的则是传入对象的本身,一般可用于多个扩展函数链式调用

inline fun <T> T.also(block: (T) -> Unit): T

let与also比较

  • let写法
fun test() {
   val array = intArrayOf(1, 2, 3, 4, 5)
   val result = array.let {
   array.forEach { println(it) }
   it.size
   }
   println(result) // 打印list数组的大小
}
  • also写法
fun test() {
   val array = intArrayOf(1, 2, 3, 4, 5)
   val result = array.also {
       array.forEach { println(it) }
   }
   println(result) //打印 array数组
}

with

不是以扩展的形式存在的,而是将某对象作为函数的参数,在函数块内可以通过 this 或省略指代该对象。返回值为函数块的最后一行或指定return表达式

inline fun <T, R> with(receiver: T, block: T.() -> R): R

可以看出with函数是接收了两个参数,分别为T类型的对象receiver和一个lambda函数块,所以with函数最原始样子如下:

val result = with(caixukun, {
    println("我是篮球大使$name, 我擅长$sing $dance $rap")
    "follow me"
})

但是由于with函数最后一个参数是一个函数,可以把函数提到圆括号的外部,所以最终with函数的调用形式如下:

val result = with(caixukun) {
    println("我是篮球大使$name, 我擅长$sing $dance $rap")
    "follow me"
}

适用于调用同一个类的多个方法时,可以省去类名重复,直接调用类的方法即可

  1. 将一个类的属性值赋给另一个新类;RecyclerView中onBinderViewHolder中,数据model的属性映射到UI上
  • 一般写法
override fun onBindViewHolder(holder: ViewHolder, position: Int){
    val item = list(position)
    holder.name.text = "名称:${item.name}"
    holder.interest.text = "兴趣:${item.age}"
}
  • with用法
override fun onBindViewHolder(holder: ViewHolder, position: Int){
   val item = list(position)
    with(item){
        holder.nameView.text = "名称:$name"
        holder.ageView.text = 兴趣:age"
    }
}
  1. with函数在使用的时候确实可以省掉很多重复的代码,还有常用的地方是点击RecyclerView 的item方法跳转到下一级页面要传递好几个参数时就可以使用with 简化.
mAdapter.setOnItemChildClickListener { _, _, position -> // id code name 均为item的属性
    with(mAdapter.data[position]) {
        ARouter.getInstance().build("/xx/XxxActivity")
                .withLong(ID, id)
                .withString(CODE, code)
                .withString(NAME, name)
                .navigation()
    }
}

run

run函数是let,with两个函数结合体,准确来说它弥补了let函数在函数体内必须使用it参数替代对象,在run函数中可以像with函数一样可以省略,直接访问实例的公有属性和方法,另一方面它弥补了with函数传入对象判空问题,在run函数中可以像let函数一样做判空处理.run函数接收一个lambda函数为参数,以闭包形式返回,返回值为最后一行的值或者指定的return的表达式

inline fun <T, R> T.run(block: T.() -> R): R

用法与let相似,这里只简单的举一个栗子.

  • 一般写法
if (list != null){
    list.forEach { 
        println(it)
    }
}
  • run 写法
list?.run{ //不需判空,内部也不需再用it代替list
   forEach { 
        println(it)
    }
}

apply

apply函数和run函数很像,唯一不同点就是它们各自返回的值不一样,apply函数的返回的是传入对象的本身,run函数是以闭包形式返回最后一行代码的值

inline fun <T> T.apply(block: T.() -> Unit): T

1.apply一般用于一个对象实例初始化的时候,需要对对象中的属性进行赋值。

举个栗子自定义view的时候对自定义属性的初始化

  • 一般用法
val typedArray = context.obtainStyledAttributes(attribute, R.styleable.ShapeView)
mFillColor = typedArray.getColor(R.styleable.ShapeView_sv_fillColor, 0xFFFFFFFF.toInt())
mStrokeColor = typedArray.getColor(R.styleable.ShapeView_sv_strokeColor, 0)
typedArray.recycle()
  • apply用法
context.obtainStyledAttributes(attribute, R.styleable.ShapeView).apply {
    mFillColor = getColor(R.styleable.ShapeView_sv_fillColor, 0xFFFFFFFF.toInt())
    mStrokeColor = getColor(R.styleable.ShapeView_sv_strokeColor, 0)
    recycle()
}
  1. 动态inflate出一个XML的View的时候需要给View绑定数据也会用到
  • 一般写法
val view = layoutInflater.inflate(R.layout.xx, null)
view.findViewById<TextView>(R.id.xx).text = title
  • apply 写法
val view = layoutInflater.inflate(R.layout.xx, null).apply {
    findViewById<TextView>(R.id.xx).text = title
}

总结

是不是可以优化自己项目中一大波代码了.逼格会不会高一点.一时用一时爽,一直用一直爽.

参考资料Kotlin 标准库

推薦閱讀更多精彩內容