5.8.8 this 表达式
原文链接: https://kotlinlang.org/docs/this-expressions.html
5.8.8 this 表达式
this 表达式引用当前接收者(即函数所作用的对象)。如何使用 this 取决于上下文:
例如,在下面的代码中,this.name 表示当前 Language 对象的 name 属性:
1
2
3
4
5
6
7
8
9
10
11
| class Language(val name: String) {
fun printName() {
println(this.name)
}
}
fun main() {
val language = Language("Kotlin")
language.printName()
// Kotlin
}
|
在下面的例子中,lastCharacter() 是在字符串 "Kotlin" 上调用的。因此 "Kotlin" 就是接收者。在扩展函数内部,this 指 "Kotlin",也就是说 this.length 是 "Kotlin" 的长度:
1
2
3
4
5
6
7
8
9
10
| fun String.lastCharacter(): Char {
println(this.length)
// 6
return this[this.length - 1]
}
fun main() {
println("Kotlin".lastCharacter())
// n
}
|
提示: 在简单情况下,你不必显式写 this。Kotlin 会从当前作用域解析它。进一步了解。
带限定符的 this
当接收者作用域嵌套时,你的代码中可能同时存在多个接收者。Kotlin 可以用任何可用的接收者隐式访问其成员,但来自内层作用域的接收者优先级更高。要显式引用某个特定的接收者,请使用带限定符的 this。当多个接收者都有同名成员、而你需要访问外层接收者的成员时,它尤其有用。
要使用带限定符的 this,请添加标签限定符:
该标签告诉编译器要访问哪个接收者。你可以使用外层类或扩展函数的名称。例如,this@foo 指名为 foo 的外层扩展函数的接收者。
从内部类访问外部类
在内部类中,不加限定的 this 指内部类实例。要访问外部类对象,请使用带限定符的 this:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
| class User(val name: String) {
inner class Age(val value: Int) {
fun printInfo() {
// 指当前 Age 对象的 value 属性
println(this.value)
// 22
// 指外层 User 对象的 name 属性
println(this@User.name)
// Jane Doe
}
}
}
fun main() {
val user = User("Jane Doe")
val age = user.Age(22)
age.printInfo()
}
|
注意: 只有内部类持有外部类实例的引用。普通的嵌套类无法访问外部类的 this。
从扩展函数访问类
如果你在类内部声明扩展函数,就有两个接收者可用:
- 扩展接收者:调用该扩展函数时所作用的值。
- 分发接收者:声明该扩展函数所在类的当前对象。
使用带限定符的 this 来指定你需要哪个接收者:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
| class User(val name: String) {
val prefix = "Name"
fun String.formatName(): String {
return "${this@User.prefix}: ${this.uppercase()}"
}
fun printName() {
println(name.formatName())
}
}
fun main() {
val user = User("Jane Doe")
user.printName()
// Name: JANE DOE
}
|
这里:
this 指扩展接收者(String),因此 this.uppercase() 会把该字符串转换为大写。this@User 指当前 User 对象。this@User.prefix 访问当前 User 对象的 prefix 属性。
从 lambda 中访问
与普通 lambda 不同,带接收者的 lambda会把一个接收者引入其作用域。因此,lambda 内部的 this 指该 lambda 的接收者,而不是外层作用域中的接收者。如果带接收者的 lambda 嵌套在另一个接收者作用域中,请给该 lambda 添加标签,并用带限定符的 this 显式引用该 lambda 的接收者或外层作用域中的接收者:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| class User(val name: String) {
fun printWithPrefix() {
val printString: String.() -> Unit = stringLabel@ {
println("${this@stringLabel}: ${this@User.name}")
}
printString("User")
}
}
fun main() {
val user = User("Jane Doe")
user.printWithPrefix()
// User: Jane Doe
}
|
这里:
stringLabel@ 为该 lambda 添加标签。this@stringLabel 指该 lambda 的字符串接收者。this@User 指当前 User 对象。
该标签不会调用任何东西,也不会改变 lambda 的工作方式,它只是帮助你引用该 lambda 的接收者。
访问匿名对象或其外部类
匿名对象有自己的接收者作用域。在对象主体内部,不加限定的 this 指该匿名对象本身。不过,由于匿名对象没有类名,你不能把它们用作 this 限定符。因此,只能用不带限定的 this 来引用该匿名对象:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
| interface UserPrinter {
fun print()
}
fun main() {
val printer = object : UserPrinter {
val prefix = "User"
override fun print() {
// `this` 指该匿名对象
// `this.prefix` 访问它的 `prefix` 属性
println(this.prefix)
}
}
printer.print()
// User
}
|
如果你在另一个类内部声明匿名对象,可以用带限定符的 this 访问外层对象:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
interface UserPrinter {
fun print()
}
class User(val name: String) {
fun createPrinter(): UserPrinter {
return object : UserPrinter {
override fun print() {
// `this@User` 指外层的 `User` 对象
// `this@User.name` 访问它的 `name` 属性
println(this@User.name)
}
}
}
}
fun main() {
val printer = User("Jane Doe").createPrinter()
printer.print()
// Jane Doe
}
|
隐式 this
当你在 this 上调用成员函数时,可以省略 this. 限定符。不过,如果更近的词法作用域中存在同名的其他可调用对象,Kotlin 会把不加限定的调用解析为那个可调用对象,而不是成员函数。要显式调用成员函数,请使用 this. 限定符:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
| fun main() {
class A {
fun printLine() {
println("Member function")
}
fun invokePrintLine() {
fun printLine() {
println("Local function")
}
printLine()
// 局部函数
this.printLine()
// 成员函数
}
}
A().invokePrintLine()
}
|