Kotlin:标准函数和静态方法 现在我们即将进入本书首次的Kotlin课堂,之后的几乎每一章中都会有这样一个环节。虽说目前 你已经可以上手Kotlin编程了,但我们只是在第2章中学习了一些Kotlin的基础知识而已,其实 还有许多的高级技巧并没有涉猎。因此每章的Kotlin课堂里,我都会结合所在章节的内容,拓展 出更多Kotlin的使用技巧,这将会是你提升自己Kotlin水平的绝佳机会。
1 标准函数with
、run
和apply
with
函数接收两个参数:第一个参数可以是一个任意类型的对象,第二个参数是一个Lambda
表达式。with
函数会在Lambda
表达式中提供第一个参数对象的上下文,并使用Lambda
表达式中的最后一行代码作为返回值返回。 示例代码如下:
val result = with(obj) {
// 这里是obj的上下文
"value" // with函数的返回值
}
这个函数的作用是:可以在连续调用同一个对象的多个方法时让代码变得更加精简。
【kotlin|Kotlin(标准函数和静态方法)】下面来看一个具体的例子:比如有一个水果列表,现在我们想吃完所有水果,并将结果打印出来,就可以这样写:
fun main() {
val list = listOf("Apple", "Banana", "Orange", "Pear", "Grape")
val builder = StringBuilder()
builder.append("Start eating fruits.\n")
for (fruit in list) {
builder.append(fruit).append("\n")
}
builder.append("Ate all fruits.")
val result = builder.toString()
println(result)
}
这段代码的逻辑很简单,就是使用
StringBuilder
来构建吃水果的字符串,最后将结果打印出来。在上述代码中连续调用了很多次
builder
对象的方法。其实这个时候就可以考虑使用with
函数来让代码变得更加精简,如下所示:val list = listOf("Apple", "Banana", "Orange", "Pear", "Grape")
val result = with(StringBuilder()) {
append("Start eating fruits.\n")
for (fruit in list) {
append(fruit).append("\n")
}
append("Ate all fruits.")
toString()
}
println(result)
首先给
with
函数的第一个参数传入了一 个StringBuilder
对象,那么接下来整个Lambda
表达式的上下文就会是这个StringBuilder
对象。于是在Lambda
表达式中就不用再像刚才那样调用builder.append()
和builder.toString()
方法了,而是可以直接调用append()
和toString()
方法。Lambda
表达式的最后一行代码会作为with
函数的返回值返回,最终将结果打印出来。这两段代码的执行结果是一模一样的,但是明显第二段代码的写法更加简洁一些,这就是
with
函数的作用。run
函数的用法和使用场景和with
函数是非常类似的,只是稍微做了一些语法改动而已。首先run
函数通常不会直接调用,而是要在某个对象的基础上调用;其次run
函数只接收一个Lambda
参数,并且会在Lambda
表达式中提供调用对象的上下文。其他方面和with
函数是一样的,包括也会使用Lambda表
达式 中的最后一行代码作为返回值返回。 示例代码如下:val result = obj.run {
// 这里是obj的上下文
"value" // run函数的返回值
}
那么现在就可以使用
run
函数来修改一下吃水果的这段代码,如下所示:val list = listOf("Apple", "Banana", "Orange", "Pear", "Grape")
val result = StringBuilder().run {
append("Start eating fruits.\n")
for (fruit in list) {
append(fruit).append("\n")
}
append("Ate all fruits.")
toString()
}
println(result)
总体来说变化非常小,只是将调用
with
函数并传入StringBuilder
对象改成了调用StringBuilder
对象的run
方法,其他都没有任何区别,这两段代码最终的执行结果是完全相同的。apply
函数和run
函数也是极其类似的,都要在某个对象上调用,并且只接收一个Lambda
参数,也会在Lambda
表达式中提供调用对象的上下文,但是apply
函数无法指定返回值,而是会自动返回调用对象本身。 示例代码如下:val result = obj.apply {
// 这里是obj的上下文
}
// result == obj
那么现在再使用
apply
函数来修改一下吃水果的这段代码,如下所示:val list = listOf("Apple", "Banana", "Orange", "Pear", "Grape")
val result = StringBuilder().apply {
append("Start eating fruits.\n")
for (fruit in list) {
append(fruit).append("\n")
}
append("Ate all fruits.")
}
println(result.toString())
注意这里的代码变化,由于
apply
函数无法指定返回值,只能返回调用对象本身,因此这里的result
实际上是一个StringBuilder
对象,所以在最后打印的时候还要再调用它的toString()
方法才行。这段代码的执行结果和前面两段仍然是完全相同的。其实
with
、run
和apply
这几个函数的用法和使用场景是非常类似的。在大多数情况下,它们可以相互转换。对于以下代码:
val intent = Intent(context, NormalActivity::class.java)
intent.putExtra("param1", data1)
intent.putExtra("param2", data2)
context.startActivity(intent)
这里每传递一个参数就要调用一次
intent.putExtra()
方法,如果要传递10
个参数,那就得调用10
次。对于这种情况,就可以使用标准函数来对代码进行精简,如下所示:val intent = Intent(context, NormalActivity::class.java).apply {
putExtra("param1", data1)
putExtra("param2", data2)
}
context.startActivity(intent)
可以看到,由于
Lambda
表达式中的上下文就是Intent
对象,所以不再需要调用intent.putExtra()
方法,而是直接调用putExtra()
方法就可以了。传递的参数越多,这种写法的优势也就越明显。2 定义静态方法
静态方法在某些编程语言里面又叫作类方法,指的就是那种不需要创建实例就能调用的方法,所有主流的编程语言都会支持静态方法这个特性。
在
Java
中定义一个静态方法非常简单,只需要在方法上声明一个static
关键字就可以了,如下所示:public class Util {public static void doAction() {
System.out.println("do action")
}}
这是一个非常简单的工具类,上述代码中的
doAction()
方法就是一个静态方法。调用静态方法并不需要创建类的实例,而是可以直接以Util.doAction()
这种写法来调用。因而静态方法非常适合用于编写一些工具类的功能,因为工具类通常没有创建实例的必要,基本是全局通用的。但是和绝大多数主流编程语言不同的是,
Kotlin
却极度弱化了静态方法这个概念,想要在Kotlin
中定义一个静态方法反倒不是一件容易的事。那么Kotlin
为什么要这样设计呢?因为Kotlin
提供了比静态方法更好用的语法特性——单例类。像工具类这种功能,在
Kotlin
中就非常推荐使用单例类的方式来实现,比如上述的Util
工具类,如果使用Kotlin
来实现的话就可以这样写:object Util {fun doAction() {
println("do action")
}}
虽然这里的
doAction()
方法并不是静态方法,但是仍然可以使用Util.doAction()
的方式来调用,这就是单例类所带来的便利性。不过,使用单例类的写法会将整个类中的所有方法全部变成类似于静态方法的调用方式,**如果只是希望让类中的某一个方法变成静态方法的调用方式该怎么办呢?这个时候就可以使用
companion object
了,**示例如下:class Util {fun doAction1() {
println("do action1")
}companion object {
fun doAction2() {
println("do action2")
}
}}
这里首先将
Util
从单例类改成了一个普通类,然后在类中直接定义了一个doAction1()
方法,又在companion object
中定义了一个doAction2()
方法。现在这两个方法就有了本质的区别,因为doAction1()
方法是一定要先创建Util
类的实例才能调用的,而 doAction2()
方法可以直接使用Util.doAction2()
的方式调用。不过,
doAction2()
方法其实也并不是静态方法,companion object
这个关键字实际上会在Util
类的内部创建一个伴生类,而doAction2()
方法就是定义在这个伴生类里面的实例方法。只是Kotlin
会保证Util
类始终只会存在一个伴生类对象,因此调用Util.doAction2()
方法实际上就是调用了Util
类中伴生对象的doAction2()
方法。由此可以看出,
Kotlin
确实没有直接定义静态方法的关键字,但是提供了一些语法特性来支持类似于静态方法调用的写法,这些语法特性基本可以满足我们平时的开发需求了。如果你确实需要定义真正的静态方法,
Kotlin
仍然提供了两种实现方式:注解和顶层方法。先来看注解,前面使用的单例类和
companion object
都只是在语法的形式上模仿了静态方法的调用方式,实际上它们都不是真正的静态方法。因此如果你在Java
代码中以静态方法的形式去调用的话,你会发现这些方法并不存在。**而如果给单例类或companion object
中的方法加上@JvmStatic
注解,那么Kotlin
编译器就会将这些方法编译成真正的静态方法,**如下所示:class Utils {fun doAction1() { }companion object {
@JvmStatic
fun doAction2() { }
}}
注意,
@JvmStatic
注解只能加在单例类或companion object
中的方法上,如果加在 一个普通方法上,会直接提示语法错误。由于
doAction2()
方法已经成为了真正的静态方法,那么现在不管是在Kotlin
中还是在Java
中,都可以使用Util.doAction2()
的写法来调用了。再来看顶层方法,顶层方法指的是那些没有定义在任何类中的方法,
Kotlin
编译器会将所有的顶层方法全部编译成静态方法,因此只要你定义了一 个顶层方法,那么它就一定是静态方法。想要定义一个顶层方法,首先需要创建一个
Kotlin
文件。任意包名右击 -> New -> Kotlin File/Class
,在弹出的对话框中输入文件名即可,注意创建类型要选择File
,如图所示:文章图片
在这个文件 中定义的任何方法都会是顶层方法,比如定义一个
doSomething()
方法,如下所示:fun doSomething() {
println("do something")
}
Kotlin
编译器会将所有的顶层方法全部编译成静态方法。如果是在Kotlin
代码中调用的话,所有的顶层方法都可以在任何位置被直接调 用,不用管包名路径,也不用创建实例, 直接键入doSomething()
即可,如图所示:文章图片
但如果是在
Java
代码中调用,是找不到doSomething()
这个方法的,因为Java
中没有顶层方法这个概念,所有的方法必须定义在类中。对于Helper.kt
这个类,Kotlin
编译器会自动创建一个叫作HelperKt
的Java
类,doSomething()
方法就是以静态方法的形式定义在HelperKt
类里面的, 因此在Java
中使用HelperKt.doSomething()
的写法来调用就可以了,如图所示:文章图片
推荐阅读
- java|JAVA语言的产生与发展史
- java|java 发展方向_Java程序员的发展方向有哪些
- java|Mybatis-Plus通用Mapper CRUD之select
- java学习|java如何一步一步运行,详细说明
- java学习|java程序员3-5年职业规划,附源代码
- java学习|java程序员具备专业技能,2022最新
- java学习|java面试基础问题答不上来怎么办,快来看鸭~
- *java常用的几种排序算法
- JUnit5教程(4): JUnit5假设、测试套件和条件测试