写点什么

kotlin 高阶函数 let、with、apply、run、also 使用场景

用户头像
陈吉米
关注
发布于: 2021 年 01 月 27 日

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()}
复制代码


发布于: 2021 年 01 月 27 日阅读数: 23
用户头像

陈吉米

关注

专注!训练专注力 2017.11.30 加入

开源NLP项目https://github.com/mayabot/mynlp

评论

发布
暂无评论
kotlin高阶函数let、with、apply、run、also使用场景