Kotlin标准库函数

Kotlin标准库中包含了几个函数,它们的目的就是可以在对象的上下文中执行代码块。当我们调用该Lambda表达式时,它会形成一个临时的作用域。在该范围内,可以访问不带名称的对象,此类函数称为作用域函数。包括:

官网地址:戳一下

这些函数的不同之处:

函数 对象引用 返回值 扩展函数
let it Lambda表达式结果
run this Lambda表达式结果
with this Lambda表达式结果
apply this 上下文对象
also it 上下文对象



下面开始介绍函数…
在这里插入图片描述

apply函数

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

这里,我们把T看做接收者的话,通过T调用apply函数,执行block块函数(lambda表达式)后,最终还是会返回T。

我么可以把它看做是配置类型的函数,返回的还是调用者本身。通过配置一些列相关函数后,返回的还是其本身。

比如,非常适合,Android中SharedPreferences或者集合操作等,会进行一系列配置任务的。

示例


val list = mutableListof().apply{
	add(1)
	add(2)
}


val adam = Person("Adam").apply { 
    age = 20                       // 同this.age = 20
    city = "London"
}
println(adam)

内部通过隐式的this来进行调用

let函数

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

把调用用T当做参数,传入block函数中,最终返回block块函数(lambda表达式)的结果

let函数使某个变量作用于lambda表达式里面,使用it关键字引用它。

let跟apply函数区别:

  • let函数会把接受者传入lambda表达式,apply则没有
  • let含税会返回lambda表达式最后一行,apply返回对象本身

示例

 fun testLet() {
        val str = "Hello"
        val result = str.let {
            it.plus(" World")
            "Done"
        }
        Log.d("liu", "result:$result")
    }

let函数执行完后,返回的就是it(最后一行代码执行的结果)

run函数

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

run函数,执行的block块函数跟apply函数一样(相当于执行调用者的扩展函数),返回的结果跟let函数一样。

只看作用域。run与apply差不多,不同点:

  • 返回值不同,run函数不返回接收者,它返回的是lambda结果(跟let一样)
  • run函数,可以执行函数引用

示例

  val numbers = mutableListOf("one", "two", "three")
val countEndsWithE = numbers.run { 
    add("four")
    add("five")
    count { it.endsWith("e") }
}
println("There are $countEndsWithE elements that end with e.")

run的lambda内部也是隐式调用了this,并且是返回最后一行代码的执行结果

这里,我们看到需要配置对象,并且带有计算要获取计算最终结果的时候,使用run函数,直接就可以完成了(apply函数无法直接获取计算结果)。

看下run函数,执行函数引用

fun testRun2() {
        val str = "Hello"
        val runResult = str.run(::containHello)

        Log.d("liu", "testRun2:$runResult")
    }

    fun containHello(str: String) = str.contains("Hello")

with函数

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

我们只看with函数后面,发现它跟run函数一模一样。并且,return的也是,执行第一个参数T的block函数块的结果。

所以,这个with函数就是run函数的一个变体,他们功能是一样的,只是调用方式不同。

with是把调用者T放到了第一个参数的位置,run函数是在外面,通过T.run来调用。

示例代码

 fun testWith() {
        val str = "Hello"
        val runResult = with(str) {
            plus("sss")
            plus("ccc")
            "run Done"
        }
        Log.d("liu", "testWith:$runResult")
    }


also函数

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

我们看到also函数跟let函数功能非常像,它也是把接收者作为值参传递给lambda。

只不过,他们的返回结果不同:also函数是返回调用者T对象本身,let返回的是block代码块执行的结果。

既然also返回的是接收者对象,我们可以基于原始接收者对象执行额外的链式调用。

示例代码

val numbers = mutableListOf("one", "two", "three")
numbers
    .also { println("The list elements before adding new one: $it") }
    .add("four")

我们先打印集合,然后,基于返回对象是原始接收者,可以接着添加元素

上面的哪些函数也是作用域函数,除了作用域函数标准库还有takeIf和takeUnless函数。这些函数可以让我们在调用链中嵌入对象状态的检查

takeIf & takeUnless

takeIf 函数需要判断lambda中提供的条件表达式,给出true或false结果。如果是ture,从takeIf返回接收者对象,如果是false,则返回null。反过来,如果对象与谓词不匹配,takeUnless将返回该对象,如果匹配,则返回null。
所以这2个函数可以当做过滤函数来使用。

val number = Random.nextInt(100)

val evenOrNull = number.takeIf { it % 2 == 0 }
val oddOrNull = number.takeUnless { it % 2 == 0 }
println("even: $evenOrNull, odd: $oddOrNull")

这2个函数跟其他函数一起时候时,是非常有用的。

例子,字符串检测

使用标准库函数


fun displaySubstringPosition(input: String, sub: String) {
    input.indexOf(sub).takeIf { it >= 0 }?.let {
        println("The substring $sub is found in $input.")
        println("Its start position is $it.")
    }
}

displaySubstringPosition("010000011", "11")
displaySubstringPosition("010000011", "12")

不使用标准库函数

fun displaySubstringPosition(input: String, sub: String) {
    val index = input.indexOf(sub)
    if (index >= 0) {
        println("The substring $sub is found in $input.")
        println("Its start position is $index.")
    }
}

displaySubstringPosition("010000011", "11")
displaySubstringPosition("010000011", "12")

函数的使用区别

看完上面的函数介绍,我们可以发现。它们都非常的相似。区别就是

  • lambda表达式的上下文对象引用方式(this或it)
  • 返回值不同(上下文对象本身或Lambda执行的结果)

上述函数,每个函数都提供了一种访问上下文对象的方式:this(作为Lambda接收器)、it(作为Lambda参数)

通过 lambda表达式的上下文对象引用方式 区分

run函数,with函数apply函数使用this作为Lambda的上下文对象。在Lambda中使用它们,对象就像在普通函数中一样,可以省略,隐式持有,从而缩短代码。所以,如果我们是在调用其(this)的属性或函数时可以使用它们。

如果忽略这一点的话,函数的差别都不大。

示例代码

val adam = Person("Adam").apply { 
    age = 20                      
    city = "London"
}
println(adam)

let函数,also函数将上下文对象作为Lambda的参数。如果,要将上下文对象当做函数调用的参数时,可以选择它们。

fun getRandomInt(): Int {
    return Random.nextInt(100).also {
        writeToLog("getRandomInt() generated value $it")
    }
}

val i = getRandomInt()
println(i)

当然,我们也可以自定义一个上下文的名字

fun getRandomInt(): Int {
    return Random.nextInt(100).also { value ->
        writeToLog("getRandomInt() generated value $value")
    }
}

val i = getRandomInt()
println(i)

通过返回值区分

  • apply函数,also函数返回的是上下文对象
  • let函数,run函数,with函数,返回的是Lambda表达式的结果

通过返回值,我们可以确认自己需要的是哪类函数了。

  • 返回上下文对象
val numberList = mutableListOf<Double>()
numberList.also { println("Populating the list") }
    .apply {
        add(2.71)
        add(3.14)
        add(1.0)
    }
    .also { println("Sorting the list") }
    .sort()

在使用链式调用的时候,可以选择它们。

  • 返回Lambda表达式的结果

当Lambda表达式的执行结果对我们有用时

val numbers = mutableListOf("one", "two", "three")
val countEndsWithE = numbers.run { 
    add("four")
    add("five")
    count { it.endsWith("e") }
}
println("There are $countEndsWithE elements that end with e.")

当我们需要通过作用域创建通用方法,来做一些事情时

val numbers = mutableListOf("one", "two", "three")
with(numbers) {
    val firstItem = first()
    val lastItem = last()        
    println("First item: $firstItem, last item: $lastItem")
}

Game Over !




在这里插入图片描述

个人感受:看完标准库函数,感觉最重要的区分就是返回值。其它的,仅仅是可以让代码更优雅一点…

猜你喜欢

转载自blog.csdn.net/ecliujianbo/article/details/128285574