Kotlin 学习之标准函数和静态方法

242 阅读4分钟

标准函数 with、run 和 apply

Kotlin 的标准函数指的是 Standard.kt 文件中定义的函数,任何 Kotlin 代码都可以自由的调用所有的标准函数。
在 Kotlin 的空安全设计学习文章中,我们学习了 let 标准函数,它的主要作用是配合 ?. 操作符进行辅助判空处理。

with 函数

with 函数接收两个参数:第一个参数可以是一个任意类型的对象,第二个参数是一个 Lambda 表达式。with 函数会在 Lambda 表达式中提供第一个参数对象的上下文,并使用 Lambda 表达式中的最后一行代码作为返回值返回。

val result = with(obj) {
    // 这里是 obj 的上下文
    "value" // with 函数的返回值
}

列举一个吃水果的例子:

val list = listOf("Apple", "Banana", "Pear")
val builder = StringBuilder()
builder.append("Start eating fruits.\n")
for (fruit in list) {
    builder.append(fruit).append("\n")
}
builder.append("Eat all fruits.")
val result = builder.toString()
println(result)

观察上述代码,会发现我们连续调用了多次 builder 对象的方法。这个时候就可以考虑使用 with 函数来简化代码:

val list = listOf("Apple", "Banana", "Pear")
val result = with(StringBuilder()) {
    append("Start eating fruits.\n")
    for (fruit in list) {
        append(fruit).append("\n")
    }
    append("Eat all fruits.")
    toString()
}
println(result)

run 函数

run 函数的用法和使用场景和 with 函数是非常类似的,稍稍有些不同。首先 run 函数是不能直接调用的,一定要调用某个对象的 run 函数才行;其次 run 函数只接收一个 Lambda 参数,并且会在 Lambda 表达式中提供调用对象的上下文。和 with 函数一样,也会使用 Lambda 表达式中的最后一行代码作为返回值返回。

val result = obj.run {
    // 这里是 obj 的上下文
    "value" // run 函数的返回值
}

使用 run 函数来修改吃水果的代码:

val list = listOf("Apple", "Banana", "Pear")
val result = StringBuilder().run {
    append("Start eating fruits.\n")
    for (fruit in list) {
        append(fruit).append("\n")
    }
    append("Eat all fruits.")
    toString()
}
println(result)

apply 函数

apply 函数和 run 函数也极其类似,都要在某个对象上调用,并且只接收一个 Lambda 参数,也会在 Lambda 表达式中提供调用对象的上下文,但是 apply 函数无法指定返回值,而是会自动返回调用对象本身。

val result = obj.apply {
    // 这里是 obj 的上下文
}
// result == obj

那么现在使用 apply 函数修改一下吃水果的代码:

val list = listOf("Apple", "Banana", "Pear")
val result = StringBuilder().apply {
    append("Start eating fruits.\n")
    for (fruit in list) {
        append(fruit).append("\n")
    }
    append("Eat all fruits.")
}
println(result.toString())

定义静态方法

Java 中定义一个静态方法非常简单,只需要在方法上声明一个 static 关键字即可。

public class Util {
    public static void doAction() {
        System.out.println("do action");
    }
}

和 Java 等大多数主流编程语言不同的是,Kotlin 却极度弱化了静态方法这个概念,想要在 Kotlin 中定义一个静态方法反倒不是一件容易的事情。

单例

object Util {
    fun doAction() {
        println("do action")
    }
}

虽然这里的 doAction() 方法并不是静态方法,但是我们仍然可以使用 Util.doAction() 的方法来调用。
不过使用单例的写法会将整个类中的所有方法都变成类似静态方法的调用方式,如果我们只是希望类中的某一个方法变成静态方法的调用方式那该怎么办?

companion object

class Util {
    fun doAction() {
        println("do action")
    }
    
    companion object {
        fun doAction2() {
            println("do action2")
        }
    }
}

doAction2() 方法其实也不是静态方法, companion object 这个关键字实际上会在 Util 类的内部创建一个伴生类。Kotlin 会保证 Util 类始终只会存在一个伴生类对象,因此调用 Util.doAction2() 方法实际上就是调用了 Util 类中伴生对象的 doAction2() 方法。

@JvmStatic 注解

前面使用的单例类和 companion object 都只是在语法的形式上模仿了静态方法的调用方式,实际上它们都不是正真的静态方法。如果我们给单例类或者 companion object 中的方法加上 @JvmStatic 注解,那么 Kotlin 编译器就会将这些方法编译成真正的静态方法。

class Util {
    fun doAction() {
        println("do action")
    }

    companion object {
        @JvmStatic
        fun doAction2() {
            println("do action2")
        }
    }
}

注意,@JvmStatic 注解只能加在单例类或者 companion object 中的方法上。

顶层方法

顶层方法是指那些没有定义在任何类中的方法。Kotlin 编译器会将所有的顶层方法都编译成静态方法。
比如我们在 Helper.kt 文件中定义一个顶层方法:

fun doSomething() {
    println("do something")
}

Kotlin 代码可以在任意位置调用顶层方法。但是在 Java 中调用的话,Helper.kt 会被自动创建成一个 HelperKt 的 Java 类,doSomething() 方法是以静态方法的形式定义在这个类中,因此在 Java 中使用 HelperKt.doSomething() 的写法来调用。