区分 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 中定义,你不必像我们在本教程中所做的那样定义它们。
相关文章
在 Java 中获取文件大小
发布时间:2023/05/01 浏览次数:139 分类:Java
-
Java 提供了不同的方法来获取文件的字节大小。 本教程演示了在 Java 中获取文件大小的不同方法。使用 Java IO 的文件类获取文件大小 Java IO 包的 File 类提供了以字节为单位获取文件大小的功能。
Java 中的文件分隔符
发布时间:2023/05/01 浏览次数:108 分类:Java
-
本篇文章介绍了 Java 中的文件分隔符。Java 中的文件分隔符 文件分隔符是用来分隔目录的字符; 例如,Unix 使用 /,Windows 使用 \ 作为文件分隔符。
Java 中的文件过滤器
发布时间:2023/05/01 浏览次数:193 分类:Java
-
本篇文章介绍如何在 Java 中使用 FileFilter。FileFilter 用于过滤具有特定扩展名的文件。 Java内置包IO和Apache Commons IO为FileFilter提供了类和接口来进行文件过滤操作。
Java 获取 ISO 8601 格式的当前时间戳
发布时间:2023/05/01 浏览次数:132 分类:Java
-
本篇文章介绍了 ISO 8601 日期格式、其重要性及其在 Java 中的使用。 它还列出了一些优点来强调为什么应该使用 ISO 格式来表示日期。
在 Java 中获取数组的子集
发布时间:2023/05/01 浏览次数:142 分类:Java
-
本篇文章介绍了几种在 Java 中获取数组子集的方法。使用 Arrays.copyOf() 方法获取数组的子集 使用 Arrays.copyOfRange() 方法获取数组的子集
用 Java 填充二维数组
发布时间:2023/05/01 浏览次数:110 分类:Java
-
二维数组是基于表结构的,即行和列,填充二维数组不能通过简单的添加到数组操作来完成。 本篇文章介绍如何在 Java 中填充二维数组。
计算 Java 数组中的重复元素
发布时间:2023/05/01 浏览次数:202 分类:Java
-
本篇文章介绍Java计算数组中重复元素的方法。计算 Java 数组中的重复元素。我们可以创建一个程序来计算数组中的重复元素。 该数组可以是未排序的,也可以是已排序的。
Java 中 List 和 Arraylist 的区别
发布时间:2023/05/01 浏览次数:90 分类:Java
-
表示为单个单元的一组单个对象称为集合。 在 Java 中,Collection 是一个具有多个已定义接口和类的框架,用于将一组对象表示为一个单元。 它允许我们操纵