let、with、apply、run、also 这几个高阶函数非常相似,看定义也非常简单,初学 kotlin 时要么不记得使用它们,要么不知道选用哪个函数。
标准库之所以设计这些高阶函数,必定有需求和使用场景,本文讨论几个常见的使用场景。解决那些习惯 Java 语言的程序员们,初次使用 kotlin 造成困扰。
1. let
1.1 经常和?.操作符号联合使用,替代 IF 判断
var name:String? = null
var map = HashMap<String>()
// name是可变变量,IF 条件判断不等于空,对于编译器来讲name还是可空变量,所以必须先赋值到val变量
val theName = name
if(theName !=null){
//此时theName是不可空变量
map[theName] = "OK"
}
// 使用?.let方法简化上面的代码
name?.let{
map[it] = "OK"
}
复制代码
2. apply
2.1 创建对象并初始化设置
val map = HashMap<String,Int>()
map["name1"] = 18
map["name2"] = 18
// 使用apply
val map = HashMap<String,Int>().apply{
this["name1"] = 18
this["name2"] = 18
}
// 如果对象创建后立即作为参数,那么避免一个变量名
fun processUser(user:User)
processUser(User().apply{
this.name = "jimi"
this.age = 18
})
复制代码
3. run
run 函数有两种:一个是全局函数、一个是扩展函数
3.1 全局函数 run 替代 java 中 { } 代码作用域
java 代码作用域有很多好处,比如避免临时变量名作用域过长,代码复制风险等,kotlin 语法层面不支持代码作用域,可以通过 run 函数替代
fun logic(){
run{
val user = User()
user.xxxx
//....
addUser(user)
}
run{
val user = User()
user.yyyy
//....
addUser(user)
}
}
// 变量名user可以重复使用,避免user1、user2这种变量命名,容易在复制代码过程中造成错误
fun logic(){
val user1 = User()
user1.xxxx
//....
addUser(user1)
val user2 = User()
user1.yyyy // 第二段代码和第一段类似,通常通过复制来修改,但是常常忘记修改user1为user2,造成bug
//....
addUser(user2)
}
复制代码
当然作用域还有其他语义更为明确等其他优势。
另外 run 也能把最好一行作为返回值,避免专门定义一个函数
val user = run{
val r1 = Role()
//.....
User(r1,...)
}
复制代码
3.2 函数懒得定义返回类型
spring mvc 中 Controller 中对应 RequestMapping 的函数是定义可以省略返回类型定义,有时候比较方便
@GetMapping("/user/list")
fun list(): List<User>{
return service.findUsers()
}
// 简写成
@GetMapping("/user/list")
fun list() = run {
service.findUsers()
}
复制代码
4 also
5.1
Java 中经常使用(line=reader.readLine())!=null 这种写法,是由于赋值语句可以作为表达式才可以这么写。
但是 kotlin 的赋值语句不能作为表达式,所以刚开始使用 kotlin 会不习惯
public void test(BufferedReader reader) throws IOException {
String line;
while ( (line=reader.readLine())!=null ){
System.out.println(line);
}
}
复制代码
在 kotlin 中
fun test(reader: BufferedReader) {
var line: String?
while (reader.readLine().also { line = it } != null) {
println(line)
}
}
复制代码
also 通过 it 访问当前变量,并且整体返回自己。所以可以模拟赋值语句当做表达式的效果。
虽然 apply 也能做类似的事情
while (reader.readLine().apply { line = this } != null)
复制代码
会造成语义不清晰,可读性不强,this 到底指的哪个对象?
选择使用 this 还是 it 访问当前对象,一个原则就是:如果使用 this,那么就不要把 this. 写出来,利用 this 关键字可以省略的特性。
5 with
5.1 消除对同一个变量多次引用
val service = Service()
service.setName("xxx")
service.doSome()
service.ddd()
service.sddd()
// 使用with修改为
with(service){
this.setName("xxx")
this.doSome()
this.ddd()
this.sddd()
}
// 进一步省略this
with(service){
setName("xxx")
doSome()
ddd()
sddd()
}
复制代码
评论