let、with、apply、run、also 这几个高阶函数非常相似,看定义也非常简单,初学 kotlin 时要么不记得使用它们,要么不知道选用哪个函数。
标准库之所以设计这些高阶函数,必定有需求和使用场景,本文讨论几个常见的使用场景。解决那些习惯 Java 语言的程序员们,初次使用 kotlin 造成困扰。
1. let
1.1 经常和?.操作符号联合使用,替代 IF 判断
var name:String? = nullvar map = HashMap<String>()
// name是可变变量,IF 条件判断不等于空,对于编译器来讲name还是可空变量,所以必须先赋值到val变量val theName = nameif(theName !=null){ //此时theName是不可空变量 map[theName] = "OK"}
// 使用?.let方法简化上面的代码name?.let{ map[it] = "OK"}
复制代码
2. apply
2.1 创建对象并初始化设置
val map = HashMap<String,Int>()map["name1"] = 18map["name2"] = 18
// 使用applyval 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()}
复制代码
评论