Kotlin标准库中,let
, run
, with
, apply
, also
这些函数在官方文档中称为 Scope Functions 。在结合 Lambda 表达式情况下,用这些函数写一些特定逻辑还是很方便的。
下面通过两个测试类阐述一下各个函数的使用方法。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28public class Car {
}
public class Person {
private String name;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public void speak(String msg) {
System.out.println(msg);
}
}
run
1 | public inline fun <R> run(block: () -> R): R |
执行 Lambda,并返回 Lambda 的结果
例如通过 Lambda 执行一些逻辑,生成某个对象并获取其引用1
2
3
4var car: Car = run {
// ......
Car()
}
这个 run
方法不需要对象,可以直接调用
T.run
1 | public inline fun <T, R> T.run(block: T.() -> R): R |
在某个对象上执行 Lambda,将此对象作为 Lambda 的接收者。
可以在 Lambda 中使用this引用,返回 Lambda 执行结果。
例如1
2
3
4var car: Car = person.run {
this.speak("person.run")
Car()
}
在 person 对象上执行 run
,可通过 this 调用 Person
类中的方法。最后返回生成的 Car
对象。
with
1 | public inline fun <T, R> with(receiver: T, block: T.() -> R): R |
和上面的 run
有些类似,不过这里不用通过 person 对象来调用了,直接调用 with
方法即可。同样返回 Lambda 执行的结果。
例如1
2
3
4val car: Car = with(person) {
this.speak("person.with")
Car()
}
T.apply
1 | public inline fun <T> T.apply(block: T.() -> Unit): T |
在给定对象上执行 Lambda,同样将这一对象作为接收者。最后返回这个对象自身。
非常适合用来创建对象,并且最后返回对象自身,可进一步调用其公开方法。感觉和 Builder 模式有些类似。在 Lambda中,调用对象自身方法可以省略 this
1
2
3
4
5
6
7var person: Person = Person().apply {
this.age = 20
name = "Jack"
}.apply {
this.speak("Hello")
speak("World")
}
T.also
1 | public inline fun <T> T.also(block: (T) -> Unit): T |
类似 apply
,区别在于将对象作为参数传进 Lambda 中,而不是作为 Lambda 的接收者。在 Lambda中以 it 作为对象引用(也可以显示指定名称)。
例如1
2
3
4
5
6
7person.also {
it.speak("person.speak")
}
person.also { he ->
he.speak("person.speak")
}
T.let
1 | public inline fun <T, R> T.let(block: (T) -> R): R |
将对象作为参数传进 Lambda,执行并返回 Lambda 结果。
例如1
2
3
4var car: Car = person.let {
it.speak("person.let")
Car()
}
T.takeIf
1 | public inline fun <T> T.takeIf(predicate: (T) -> Boolean): T? |
将对象作为参数传进 Lambda,当 Lambda 执行结果 true 时,返回此对象,否则返回 null
例如1
2
3
4
5
6var child: Person? = Person().apply {
age = 20
}.takeIf {
it.age < 18
}
println(child)
因为年龄不小于18,打印结果是 null
T.takeUnless
1 | public inline fun <T> T.takeUnless(predicate: (T) -> Boolean): T? |
和 takeIf
相反,当 Lambda 执行结果为 true 时,返回 null,否则返回此对象
例如1
2
3
4
5
6var adult: Person? = Person().apply {
age = 20
}.takeUnless {
it.age < 18
}
println(adult)
打印结果不为空
repeat
1 | public inline fun repeat(times: Int, action: (Int) -> Unit) |
重复执行 Lambda N 次,将循环 Index 传进 Lambda中
例如1
2
3repeat(3) {
person.speak("speak $it times")
}
打印结果
speak 0 times
speak 1 times
speak 2 times
方法的选择
Kotlin官方文档中介绍了这些函数对应的场景。同时建议不要过度使用这些函数,以避免降低代码可读性。
Funtion | Object reference | Return value | Is extension funtion |
---|---|---|---|
let | it | Lambda result | Yes |
run | this | Lambda result | Yes |
run | - | Lambda result | No:called without the context object |
with | this | Lambda result | No:takes the context object as an argument |
apply | this | Context object | Yes |
also | it | Context object | Yes |
- Executing a lambda on non-null objects: let
- Introducing an expression as a variable in local scope: let
- Object configuration: apply
- Object configuration and computing the result: run
- Running statements where an expression is required: non-extension run
- Additional effects: also
- Grouping function calls on an object: with