区分 Kotlin 中的扩展函数

David Mbochi Njonge 2023年1月30日
  1. 在 Kotlin 中创建扩展函数
  2. 在 Kotlin 中使用 Lambda 函数和扩展函数
  3. 在 Kotlin 中使用 let() 扩展函数
  4. 在 Kotlin 中使用 also() 扩展函数
  5. 在 Kotlin 中使用 apply() 扩展函数
  6. 在 Kotlin 中使用 takeIf() 扩展函数
  7. 在 Kotlin 中使用 takeUnless() 扩展函数
  8. 结论
区分 Kotlin 中的扩展函数

在面向对象编程中,我们学习了继承、封装、抽象和多态的概念,但这些不足以以灵活、可重用和可维护的方式构建我们的应用程序。

设计模式,在设计原则和 OOP 概念的帮助下,提供了解决方案来解决常见的设计问题。一种与我们将在本教程中介绍的非常相似的设计模式是装饰器设计模式。

装饰器设计模式允许我们向应用程序添加功能,而无需从另一个类扩展。在 Kotlin 中,我们可以通过使用 Kotlin 扩展函数来实现相同的功能设计模式。

Kotlin 中的扩展函数向一个类添加功能,而无需从另一个类扩展或使用装饰器设计模式。在幕后,扩展函数可以是泛型的,可以使用 lambda 函数来实现我们提到的功能。

在本教程中,我们将深入了解每个扩展函数,并为每个函数提供一个示例。本文介绍的扩展函数包括:apply()also()let()takeIf()takeUnless()

在 Kotlin 中创建扩展函数

要创建扩展函数,请定义要添加功能的类型,例如字符串、数字或类,并使用点运算符定义委托功能的方法。定义扩展函数的返回类型,最后在同一行实现方法的逻辑。

转到 Intellij 代码编辑器并创建一个名为 extension-exercise 的新项目。在 src 下,创建文件夹结构 com.extension

extension 文件夹下创建一个 Main.kt 文件并粘贴以下代码。

package com.extension

fun Number.isGreaterThanTen(): Boolean = this.toInt() > 10;

fun main() {
    val  num = 100;
    println(num.isGreaterThanTen());
}

在上面的代码中,我们在 Number 类型上定义了一个名为 isGreaterThanTen() 的扩展函数,它检查一个数字是否大于 10 并返回一个布尔值。

如果你从此时开始调用任何 number 类型的变量,你将可以访问 isGreaterThanTen() 方法,如上面的 main 方法所示。

请注意,当你运行上述程序时,会在控制台中记录布尔值 true

true

在 Kotlin 中使用 Lambda 函数和扩展函数

在幕后,一个 lambda 函数可以定义为一个只包含一个方法的接口。lambda 函数匿名传递给具体函数,无需指定函数名称,然后使用箭头符号定义返回类型。

创建另一个文件并将之前的代码移动到该文件中。将以下示例复制并粘贴到空的 Main.kt 文件中。

package com.extension

fun Number.isGreaterThanTen(block: (Number) -> Boolean): Boolean = block(this);

fun main() {
    val num = 100;
    println(num.isGreaterThanTen { number ->
        number.toInt() > 10
    })

}

在上面的代码中,我们定义了一个名为 isGreaterThanTen() 的扩展函数,它接受一个 lambda 函数作为参数并返回一个布尔值。

我们命名为 block 的 lambda 函数接受 Number 类型的参数并返回一个布尔值。lambda 函数返回的这个布尔值被委托给扩展函数。

与前面的示例相比,我们在对主函数内的任何 Number 类型的变量调用 isGreaterThanTen() 后定义扩展函数的逻辑。

运行上面的代码,观察我们得到的结果和前面的例子一样。输出如下图所示。

true

在 Kotlin 中使用 let() 扩展函数

将之前的代码移动到我们创建的新文件中,并将以下代码粘贴到 Main.kt 文件中。

package com.extension

inline fun <T, R> T.let(block: (T) -> R): R = block(this);

fun  main(){
    val num = 10;
    val result = num.let { number -> number.toString() }
    println(result);
    println(result::class.java.typeName);
}

let() 函数是使用泛型定义的,这意味着它可以使用任何类型。调用 let() 函数的对象作为 lambda 函数的参数传递。

lambda 函数在 let() 函数内部定义,并返回与作为参数传递的对象不同的对象。

由于 let() 函数返回的值与 lambda 函数返回的函数相同,我们将 lambda 函数返回值委托给 let() 函数。

在 main 方法中,我们定义了一个 Number 类型的变量,并调用我们的扩展函数来返回数字的字符串表示形式。

然后,我们将返回的值及其类型记录到控制台,以验证扩展功能是否按要求工作。请注意,传递给扩展函数的类型是数字,但返回的值是字符串,如下所示。

10
java.lang.String

在 Kotlin 中使用 also() 扩展函数

将之前的代码移动到我们创建的新文件中,并将以下代码粘贴到空的 Main.kt 文件中。

package com.extension

inline fun <T> T.also(block: (T) -> Unit): T {block(this); return  this}

fun main(){
    val  num = 10;
    num.also {i ->
        println(i == 10)
    }
}

调用 also() 扩展函数的对象作为 lambda 函数的参数传递。lambda 函数被定义为扩展函数的参数,并且不返回任何通常由 Unit 表示的值。

also() 函数返回你正在使用的当前变量,该变量被委托给 lambda 函数以进行进一步计算。在 main 函数中,我们定义了一个 Number 类型的变量,委托给 lambda 函数来检查值是否等于 10。

运行代码并注意它返回一个真值,如下所示。

true

在 Kotlin 中使用 apply() 扩展函数

将之前的代码移动到我们创建的新文件中,并将以下代码粘贴到空的 Main.kt 文件中。

package com.extension

inline fun <T> T.apply(block: T.() -> Unit): T {block(); return this}

fun main(){
    val num = 10;
    num.apply {
        println(toDouble());
    }
}

调用 apply() 扩展函数的对象作为该对象的方法调用传递给 lambda 函数的参数。lambda 函数没有返回值。

该定义意味着我们不必使用类型的对象来调用方法,因为 lambda 函数会处理该功能。在这种情况下,我们只需要从一个类型中调用我们需要的方法。

在主函数中,我们定义了一个 Number 类型的变量,并使用 apply() 方法,我们通过调用 toDouble() 方法将 Number 转换为 Double。请注意,没有使用对对象的引用来调用 toDouble() 方法。

运行上面的代码,注意记录到控制台的值是 Double,如下图。

10.0

在 Kotlin 中使用 takeIf() 扩展函数

将之前的代码移动到我们创建的新文件中,并将以下代码粘贴到空的 Main.kt 文件中。

package com.extension

inline fun <T> T.takeIf(predicate: (T) -> Boolean): T? = if (predicate(this)) this else null

fun main() {
    val  num = 10;
    println(num.takeIf { i ->
        i.plus(10) < 30
    })
}

调用 takeIf() 扩展函数的对象作为 lambda 函数的参数传递。lambda 函数作为此方法的参数传递,并返回一个布尔值。

仅当 lambda 表达式的条件计算为 true 时,扩展函数才返回调用对象。否则,它返回一个 null 值。

在主函数中,我们定义了一个 Number 类型的值,它使用 takeIf() 来确定这个数字加 10 是否小于 30。如果条件评估为真,则返回值 10;否则,返回一个 null 值。

运行上面的代码,注意 lambda 表达式的计算结果为 true,并返回值 10,如下所示。

10

在 Kotlin 中使用 takeUnless() 扩展函数

将之前的代码移动到我们创建的新文件中,然后将以下代码复制并粘贴到空的 Main.kt 文件中。

package com.extension

inline fun <T> T.takeUnless(predicate: (T) -> Boolean): T? = if (!predicate(this)) this else null

fun main() {
    val num = 10;
    println(num.takeUnless { i ->
        i.plus(10) < 30
    })
}

调用 takeUnless() 扩展函数的对象作为 lambda 函数的参数传递。lambda 函数在此方法中定义并返回一个布尔值。

takeIf()takeUnless() 之间的区别在于,此示例仅在 lambda 表达式的计算结果为 false 时返回一个值。这意味着扩展函数返回的值是 lambda 函数返回的布尔函数的倒数。

在 main 方法中,我们定义了与上面相同的示例,但请注意,此示例将返回 null,因为 lambda 函数的计算结果为 true,它被反转为 false

null

结论

本教程教我们如何使用扩展函数在 Kotlin 中添加功能。涵盖的扩展函数包括:使用 let()also()apply()takeIf()takeUnless()

请注意,这些扩展函数已经在 Kotlin API 中定义,你不必像我们在本教程中所做的那样定义它们。

David Mbochi Njonge avatar David Mbochi Njonge avatar

David is a back end developer with a major in computer science. He loves to solve problems using technology, learning new things, and making new friends. David is currently a technical writer who enjoys making hard concepts easier for other developers to understand and his work has been published on multiple sites.

LinkedIn GitHub