Kotlin标准函数:
fun main() {
val student = Student("lucky", 19);
study(student)
}
fun study(student: Student?) {
study?.doHomework()
study?.readBooks()
}
在Kotlin学习 - 可空系统类型中,如果入参是可以为空的,那么函数中每次调用该对象函数都需要加?.
,代码编写太过繁琐。这个时候就可以结合使用?.
操作符和let
函数来对代码进行优化了,如下所示:
fun main() {
val student = Student("lucky", 19);
study(student)
}
fun study(student: Student?) {
student?.let { s ->
s.doHomework()
s.readBooks()
}
}
?.
操作符表示对象为空时什么都不做,对象不为空时就调用let
函数。之前做lamda表达式优化的时候讲(Kotlin学习 - 集合):当Lambda 表达式的参数列表中只有一个参数时,也不必声明参数名,而是可以使用it关键字来代替,因此上面代码可以优化下:
fun main() {
val student = Student("lucky", 19);
study(student)
}
fun study(student: Student?) {
student?.let {
it.doHomework()
it.readBooks()
}
}
let
除了可以帮我们简化非空判断,在多线程中还可以控制处理全局变量的判空问题。
var student: Student? = null
fun study() {
if (student !=null){
student.doHomework()
student.readBooks()
}
}
上面代码是会编译报错的,是因为全局变量的值随时都有可能被其他线程所修改,即使做了判空处理,仍然无法保证if
语句中的student
变量没有空指针风险。使用let
函数修改下:
var student: Student? = null
fun study() {
student?.let {
it.doHomework()
it.readBooks()
}
}
报错消失,判断student
变量不为空后,执行let中的语句,let可确保lambda语句执行的过程中student
变量一直不为空。
总结:let
可以帮我们简化非空判断,在多线程中还可以控制处理全局变量的判空问题。
fun main() {
val fruits = listOf("apple", "banana", "pear")
println(fruitPrint(fruits))
}
上面有个水果列表,需求是按固定的格式拼接起来,惯常实现方法如下:
fun fruitPrint(fruits: List<String>): String {
val fruitPrint = StringBuilder();
fruitPrint.append("My favorite fruits are : \n")
for (f in fruits) {
fruitPrint.append(f).append("\n")
}
fruitPrint.append("----- end -----")
return fruitPrint.toString()
}
//打印结果
My favorite fruits are :
apple
banana
pear
----- end -----
在拼接字符串的地方连续调用了多次StringBuilder
对象。针对这种场景我们可以用with
函数来让代码变得更加精简。
fun fruitPrint(fruits: List<String>): String {
return with(StringBuilder()) {
append("My favorite fruits are : \n")
for (f in fruits) {
append(f).append("\n")
}
append("----- end -----")
toString()
}
}
给with
函数的第一个参数传入了一个StringBuilder
对象,那么整个Lambda 表达式的上下文就会是这个StringBuilder
对象。于是我们在Lambda 表达式可以直接调用append()
和toString()
方法。Lambda 表达式的最后一行代码会作为with
函数的返回值返回。
总结:需要同一对象频繁操作的时候,可以使用with
函数精简代码。
run
函数的用法和with
函数是非常类似的,不过run
函数通常不会直接调用,而是要在某个对象的基础上调用。而且with
有两个入参,run
只有一个Lambda表达式入参。
同样的是,with
和run
的Lambda 表达式中的最后一行代码作为返回值返回。
上面例子用run
函数重写下:
fun fruitPrint(fruits: List<String>): String {
return StringBuilder().run {
append("My favorite fruits are : \n")
for (f in fruits) {
append(f).append("\n")
}
append("----- end -----")
toString()
}
}
apply
函数和run
函数用法类似,都要在某个对象上调用,并且只接收一个Lambda 参数,也会在Lambda 表达式中提供调用对象的上下文,但是apply函数无法指定返回值,而是会自动返回调用对象本身。
fun fruitPrint(fruits: List<String>): String {
val sb = StringBuilder().apply {
append("My favorite fruits are : \n")
for (f in fruits) {
append(f).append("\n")
}
append("----- end -----")
}
return sb.toString()
}
由于apply
函数无法指定返回值,只能返回调用对象本身,因此无法直接返回,我们的返回值声明的是String类型,因此还需要转化下。