天天看点

kotlin中标准函数的使用(with、also、aply、let、run)一、简介二、let三、with三、run四、apply五、also六、 run的两种方式为什么this作用域不一样,一个是 调用该函数所在的实例,一个是T对象本身实例七、为什么let 和 also使用it代替本身,而 with、run和apply 使用this 或省略代表本身八、五个方法对比和比较

目录

一、简介

二、let

三、with

三、run

四、apply

五、also

六、 run的两种方式为什么this作用域不一样,一个是 调用该函数所在的实例,一个是T对象本身实例

七、为什么let 和 also使用it代替本身,而 with、run和apply 使用this 或省略代表本身

八、五个方法对比和比较

一、简介

Standard.kt是Kotlin库的一部分,它定义了一些基本函数,主要包括五个常用方法:run、with、apply、also、let

二、let

看下 let的源码

@kotlin.internal.InlineOnly
public inline fun <T, R> T.let(block: (T) -> R): R {
    contract {
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    return block(this)
}
           

定义了一个T类型对象,可以调用let方法,参数传了一个lambda函数,该参数block为 T类型参数,返回值是R类型, 然后fun let 返回值类型也是R。

根据lamda语法特点,可以用it代指对象本身,返回值为函数块的最后一行或指定return表达式。

总结就是 使用方式为

(1) 不可为空变量

var t:T = T()
t.let{
  it.a()
  it.b()
  it.c()
}
           

(2) 可为空变量

var t:T? = null
t?.let{
 it.a()
 it.b()
 it.c()
}
           

(3) 返回值 为代码块最后一句 或者指定 return

@JvmStatic
    fun main(args: Array<String>) {
        val result = "let".let {
            println(it.length)
            "let"
        }
        println(result)
    }

运行结果
3
let
           

(4)最常用的是 可空变量 多个操作处理,不用判断空异常

(5)特点:let函数 用it代表调用对象本身,不能使用this或省略,返回值为函数体最后一句或者return指定返回

三、with

看下with源码:

@kotlin.internal.InlineOnly
public inline fun <T, R> with(receiver: T, block: T.() -> R): R {
    contract {
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    return receiver.block()
}
           

 与let不同的是,let定义的是  T.let, 这里没有定义 T.with,而是定义with,传参是 T对象本身 和 lamda函数 block,根据lamda简化规则,方法最后一个参数是lamda表达式,且唯一,可以把表达式拿出去。所以一般使用如下

with(loginData){
            println(this?.token)
            println(this?.userId)
            println(this?.userName)
        }
           

可以调用this,这里 loginData是可空变量,如果非可空,还可以省略this,所以with有个缺点就是如果传入对象为可空对象比较麻烦,没有空处理,后面也是lamda表达式和let一样 返回值也是 函数体最后一句或者是return指定

(1)使用场景:用于调用同一个类的多个方法时,如果比较懒连it都不想写,可以省去类名重复,直接调用类的方法就行,经常用于适配器中,例如RecycleView的onBinderViewHolder中。

代码示例:

orderInfo?:return
        with(orderInfo){
            helper.setText(R.id.tv_order_total_money, orderPrice.toString())
            helper.setText(R.id.tv_order_total_discounts_money, presentPrice.toString())
            helper.setText(R.id.tv_order_total_real_money, actualPrice.toString())
        }
           

(2) 特点:with函数需要传对象, 用this或省略代表对象本身,没有做空异常处理,返回值为函数体最后一句或者return指定返回

三、run

看下run的源码

@kotlin.internal.InlineOnly
public inline fun <R> run(block: () -> R): R {
    contract {
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    return block()
}

@kotlin.internal.InlineOnly
public inline fun <T, R> T.run(block: T.() -> R): R {
    contract {
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    return block()
}
           

有两个方法,直接调用 run 代表作用对象为外部类对象,T.run 代表作用对象为T类型对象。

 第一个方法:

var name = "123"
        println(name)
        run{
            name = "234"
            println(name)
        }
        println(name)

结果是:
123
234
234
           

第二个方法

var loginData:LoginData?=null
        loginData?.let {
            it.token = "1jdifjidfjidfj"
            it.userId = "1001"
            it.userName = "123"
        }
        loginData?.run {
            token = "1jdifjidfjidfj"
            userId = "1001"
            userName = "123"
        }
           

run可以像let一样有使用空判断

run结合了 let和with两者特点,所以 能用let 和 with写的都能用 run写,run也是可以用this和省略代指调用对象本身,可以省略,然后返回值类型也是函数体 最后一句 或retrun指定

(1)使用场景:let和with使用的所有场景

(2)特点:run函数是let和with的结合体, 用this或省略代表对象本身,可以做空异常处理,返回值为函数体最后一句或者return指定返回

四、apply

看源码:

@kotlin.internal.InlineOnly
public inline fun <T> T.apply(block: T.() -> Unit): T {
    contract {
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    block()
    return this
}
           

可以看到 最后有指定返回值 this,返回本身,所以apply和 上面let、with、run 在返回上面有明显区别,然后传参是一个 无返回值lambda表达式,表达式和run表达式类似,也是用this或者省略代指本身。

(1)使用场景:

需要对对象本身做一些操作,然后返回对象本身,对象实例初始化,需要对对象属性初始化赋值,或者动态inflate出一个XML的View的时候需要给View绑定数据也会用到,这种情景非常常见。

bottomView = View.inflate(activity, R.layout.view_bottom, null).apply{
   tv_title = "123"
   tv_name = "djfidfj"
   seek_bar.max = 100
   seek_bar.progress = 0
}
           

需要做多级判空处理:

kotlin中标准函数的使用(with、also、aply、let、run)一、简介二、let三、with三、run四、apply五、also六、 run的两种方式为什么this作用域不一样,一个是 调用该函数所在的实例,一个是T对象本身实例七、为什么let 和 also使用it代替本身,而 with、run和apply 使用this 或省略代表本身八、五个方法对比和比较

(2)总结:大体与run相似,也是this或者省略代表本身,可以做空异常处理,返回值为本身,可以做链式调用处理

五、also

 also源码:

@kotlin.internal.InlineOnly
@SinceKotlin("1.1")
public inline fun <T> T.also(block: (T) -> Unit): T {
    contract {
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    block(this)
    return this
}
           

我们发现also和let很相似,最后都是用it代表对象本身,只不过返回值不一样,let是返回函数体内最后一行的值,如果最后一行为空就返回一个Unit类型的默认值。also是返回本身

kotlin中标准函数的使用(with、also、aply、let、run)一、简介二、let三、with三、run四、apply五、also六、 run的两种方式为什么this作用域不一样,一个是 调用该函数所在的实例,一个是T对象本身实例七、为什么let 和 also使用it代替本身,而 with、run和apply 使用this 或省略代表本身八、五个方法对比和比较

(1)使用场景:多个扩展函数链式调用

(2)总结:大体与let相似,也是it代表本身,可以做空异常处理,返回值为本身,可以做链式调用处理

六、 run的两种方式为什么this作用域不一样,一个是 调用该函数所在的实例,一个是T对象本身实例

我们来看看两种方式源码有啥区别:

@kotlin.internal.InlineOnly
public inline fun <R> run(block: () -> R): R {
    contract {
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    return block()
}

@kotlin.internal.InlineOnly
public inline fun <T, R> T.run(block: T.() -> R): R {
    contract {
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    return block()
}
           

这里可以看到 一个是() ->R, 一个是 T.() ->R,

所以回调函数里面 this一个是代表调用run方法的实例,

另一个函数里面this作用域是 类型T的实例

七、为什么let 和 also使用it代替本身,而 with、run和apply 使用this 或省略代表本身

我们来看看源码区别:

let和also 中lambda表达式是:
block: (T) -> R 和 block: (T) -> Unit
前面都是传 (T)
而with、run和apply中的lambda表达式是:
 block: T.() -> R、block: T.() -> R和 block: T.() -> Unit
前面都是传 T.()

一个是传(T),一个是传T.()这个区别
           

 只有T.() 这样才代表 回调方法里面this作用域为 T对象本身,而this又可以省略。而(T) 这样 回调方法里面的不能用this代表T对象本身,所以用一个t指代 T对象本身

八、五个方法对比和比较

名称 源码 函数体内什么代表本身 返回值 是否是扩展函数 适用的场景
let
@kotlin.internal.InlineOnly
public inline fun <T, R> T.let(block: (T) -> R): R {
    contract {
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    return block(this)
}      
it 闭包形式返回 适用于处理不为null的操作场景
with
@kotlin.internal.InlineOnly
public inline fun <T, R> with(receiver: T, block: T.() -> R): R {
    contract {
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    return receiver.block()
}      
this或省略 闭包形式返回 适用于调用同一个类的多个方法时,可以省去类名重复,直接调用类的方法即可,经常用于Android中RecyclerView中onBinderViewHolder中,数据model的属性映射到UI上
run
(1)
@kotlin.internal.InlineOnly
public inline fun <R> run(block: () -> R): R {
    contract {
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    return block()
}
      
(2)
@kotlin.internal.InlineOnly
public inline fun <T, R> T.run(block: T.() -> R): R {
    contract {
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    return block()
}      
this或省略 闭包形式返回 适用于let,with函数任何场景。
apply
@kotlin.internal.InlineOnly
public inline fun <T> T.apply(block: T.() -> Unit): T {
    contract {
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    block()
    return this
}      
this或省略 返回this

1、适用于run函数的任何场景,一般用于初始化一个对象实例的时候,操作对象属性,并最终返回这个对象。

2、动态inflate出一个XML的View的时候需要给View绑定数据也会用到.

3、一般可用于多个扩展函数链式调用

4、数据model多层级包裹判空处理的问题

also
@kotlin.internal.InlineOnly
@SinceKotlin("1.1")
public inline fun <T> T.also(block: (T) -> Unit): T {
    contract {
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    block(this)
    return this
}      
it 返回this 适用于let函数的任何场景,一般可用于多个扩展函数链式调用

参考文档:https://blog.csdn.net/u013064109/article/details/78786646

继续阅读