Kotlin run、with、apply、also、let、takeIf、takeUnless、repeat的使用及区别

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/mengks1987/article/details/84144044


网上有关于run、with、apply、also、let、takeIf、takeUnless、repeat的使用及区别并且已经总结好了,但通过死记硬背用不了多久我们就会忘记,而且由于这几个函数比较像,我们很容易弄混了,下面我们通过分析源代码的角度去学习这个函数的使用及区别。下面我们以run来举例:
在这里插入图片描述
下面1、2、3对应上图红色方框1、2、3处
1、T.run 表示:此函数是扩展函数,任意对象都可调用。
2、接收一个函数,并且函数内任意调用改对象的属性、方法
3、返回函数内的return

我们对这几处的标记都清楚后我们具体看看run、with、apply、also、let、takeIf、takeUnless、repeat的功能。

run

/**
 * Calls the specified function [block] and returns its result.
 */
@kotlin.internal.InlineOnly
public inline fun <R> run(block: () -> R): R {
    contract {
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    return block()
}

/**
 * Calls the specified function [block] with `this` value as its receiver and returns its result.
 */
@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的用法如下:

	val person = Person()
        person.run { 
            age = 1
            sex = 3
        }

        var e = run{
            var a =""
            2+3
        }

class Person(var name: String = "", var age: Int = 0, var sex: Int = 0)

T.run是扩展函数,任何对象都可以调用,大括号内可以直接调用对象的属性和方法。
run:返回大括号内最后一行
注:有的文章中说可以使用return返回,如下:

 var e = run{
            var a =""
            return 2+3
        }

但我这无法编译通过,有可能是版本的问题。

with

/**
 * Calls the specified function [block] with the given [receiver] as its receiver and returns its result.
 */
@kotlin.internal.InlineOnly
public inline fun <T, R> with(receiver: T, block: T.() -> R): R {
    contract {
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    return receiver.block()
}

我们发现with也不是扩展函数,而且有2个参数,返回block函数的返回值
用法:

with(person) {
            age = 1
            this.sex = 3
        }

1、block函数内this代表with的参数,所以可以直接调用persion的属性、方法
2、从用法上我们发现persion不能为null
3、this可以省略

apply

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

1、apply返回的是对象本身
2、block函数内this代表对象本身

使用:

val newPersion = person.apply { this.age = 0}
                .apply { sex =2 }

是不是感觉可以替代Builder模式

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
}

和apply对比发现是block的参数不一样,apply是T.(),also是(T),所以使用是不同的:

val b = person.also {it.age =4}
                .also { it.sex = 5 }

also中不可以直接调用对象的属性和方法,通过it调用,it代表当前对象

let

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

和also对比不同是返回值不同,also返回当前对象,let返回block函数最后一行

takeif

@kotlin.internal.InlineOnly
@SinceKotlin("1.1")
public inline fun <T> T.takeIf(predicate: (T) -> Boolean): T? {
    contract {
        callsInPlace(predicate, InvocationKind.EXACTLY_ONCE)
    }
    return if (predicate(this)) this else null
}

如果函数返回值是true则返回对象本身,否则返回null
使用:

var p = person.takeIf { true }

takeUnless

@kotlin.internal.InlineOnly
@SinceKotlin("1.1")
public inline fun <T> T.takeUnless(predicate: (T) -> Boolean): T? {
    contract {
        callsInPlace(predicate, InvocationKind.EXACTLY_ONCE)
    }
    return if (!predicate(this)) this else null
}

takeUnless和takeif相反,使用:

var p = person.takeUnless { true }

repeat

@kotlin.internal.InlineOnly
public inline fun repeat(times: Int, action: (Int) -> Unit) {
    contract { callsInPlace(action) }

    for (index in 0 until times) {
        action(index)
    }
}

就是封装了一个for循环,使用:

repeat(4){
            //it代表 0..3
            print(it)
        }

总结

看完上面的是不是特别晕,感觉没什么区别啊,下面总结下他们的区别和相同点,我觉得没必要背下来,我们需要通过源码理解他的意思。

1、他们都是作用域函数,都提供了内部作用域
2、函数前没有T的是普通函数(run,with)和有T的是扩展函数(T.run,T.also 等)
3、参数不同

block:T.() 参数当前对象,this可以省略
blcok:(T): 参数当前对象,it表示当前对象,不可省略
无参数的形式:block: ()

4、返回值不同:

return block():返回block函数最后一行
return this:返回当前对象

使用场景

在这里插入图片描述

具体代码中的使用

1、属性设置(包括自定义类和系统对象)

原来的写法:

var person = Person()
        person.age = 1
        person.name = ""
        person.sex = 4

let写法 :

person.let {
            it.name = ""
            it.age = 1
            it.sex = 3
        }

run写法(推荐):

person.run {
            name = ""
            age = 1
            sex = 3
        }

系统属性:

	textView.run { 
            textSize = 12F
            text = ""
        }

是不是run的写法更“漂亮”。

2、需要null判断的地方

if(null != textView){
            print("textView is not null")
            add()
        }

run、let写法:

textView?.let {
            print("textView is not null")
            add()
        }
        textView?.run {
            print("textView is not null")
            add()
        }

猜你喜欢

转载自blog.csdn.net/mengks1987/article/details/84144044