1. 概述
在 Kotlin 中,我们已经知道关键字 this
用于引用当前正在操作的对象。
本文将深入探讨 this
的两个关键概念:隐式 this 与 限定 this(Qualified this),帮助我们更好地理解在不同上下文中 this
的行为与使用方式。
2. 接收者(Receiver)
this
的接收者是指使用 this
时所处的上下文对象。
例如,在类的成员函数中使用 this
,它指向的就是该类的实例对象:
class Outer {
fun checkThis() {
val b = this
}
}
在这个例子中,this
指向的就是 Outer
类的一个实例。
如果你使用 IntelliJ IDEA 等 IDE,可以通过快速跳转查看 this
所指向的具体对象,从而更直观地理解接收者的含义。
3. 隐式 this(Implicit this)
隐式 this 是指在无需显式使用 this
的情况下,默认指向当前类、函数或 lambda 表达式的接收者。
隐式 this 的默认接收者是当前类的实例。例如,在类的成员函数中,this
默认指向该类的实例。
3.1. 访问当前类实例
隐式 this 最简单的用法就是直接引用当前类实例:
class Outer {
val a = this
fun checkThis() {
val b = this
fun checkThisToo() {
val c = this
}
}
}
可以看到,即使函数嵌套多层,this
依然指向的是最外层 Outer
类的实例。
3.2. 访问当前类的成员
我们可以使用 this
来访问类的成员变量和方法:
class Outer {
fun printLine() = "Member function"
val x = 0
fun checkThis() {
assertEquals(0, this.x)
assertEquals(0, x)
assertEquals("Member function", this.printLine())
assertEquals("Member function", printLine())
}
}
✅ 注意:在调用成员变量或方法时可以省略 this
,但如果有同名的局部变量或顶层函数,则可能会造成歧义。
例如:
val x = 5
fun printLine() = "Top-level function"
class Outer {
fun printLine() = "Member function"
val x = 0
fun checkThis() {
assertEquals(0, this.x)
assertEquals(5, x) // 实际访问的是顶层变量 x
assertEquals("Member function", this.printLine())
assertEquals("Top-level function", printLine()) // 实际调用的是顶层函数
}
}
⚠️ 踩坑提示:当存在同名变量或函数时,如果不加 this
,编译器可能会调用错误的对象,导致难以察觉的 bug。
3.3. 在扩展函数中
在扩展函数中,this
指向的是被扩展的对象:
class Outer {
inner class Inner {
fun Int.foo() {
val y = this
assertEquals(42, this)
val increase = fun(x: Int): Int {
return this + x
}
}
}
}
val outer = Outer()
val inner = outer.Inner()
inner.run {
42.foo()
}
在这个例子中,this
指向的是 Int
类型的值 42。
3.4. 在 Lambda 表达式中
Lambda 表达式中的 this
取决于其定义的上下文:
- 如果定义在类中,
this
指向该类的实例。 - 如果定义在对象中,
this
指向该对象。 - 如果定义在扩展函数中,
this
指向扩展函数的接收者。
class Outer {
inner class Inner {
fun checkInner() {
val funInnerLambda = { _: String ->
val r = this
assertEquals(Inner::class.java.name, r::class.java.name)
}
funInnerLambda("test inner lambda")
val someNumber = 10
someNumber.run {
val numberLambda = { _: String ->
assertEquals(someNumber, this)
}
numberLambda("numberLambda")
}
val someObject = object {
val name = "John"
val age = 30
}
someObject.run {
assertEquals(someObject, this)
val someLambda = { _: String ->
assertEquals(someObject, this)
assertEquals(30, this.age)
assertEquals("John", this.name)
}
someLambda("someLambda")
}
}
fun Int.foo() {
val funExtLambda = { _: String ->
val r = this
assertEquals("Int", r::class.simpleName)
}
funExtLambda("test funExtLambda")
}
}
}
在这个例子中,this
在不同的 lambda 中分别指向了 Inner
实例、someNumber
、someObject
和扩展函数中的 Int
对象。
4. 限定 this(Qualified this)
限定 this 是指使用 this@ClassName
的方式来明确指定当前 this
所指向的类实例。
这种方式可以避免由于嵌套类、扩展函数或 lambda 表达式中 this
指向模糊而导致的歧义。
4.1. 明确访问特定类
通过 this@ClassName
,我们可以明确指定要访问的类实例:
class Outer {
inner class Inner {
fun Int.foo() {
assertEquals(Inner::class.java.name, this@Inner::class.java.name)
assertEquals(Outer::class.java.name, this@Outer::class.java.name)
}
}
}
在这个例子中,this@Inner
明确指向 Inner
类的实例,而 this@Outer
指向的是外层 Outer
类的实例。
4.2. 明确访问外层类成员
在嵌套类中,我们可以通过限定 this
来访问外层类的成员:
class Outer {
fun foo(): Int = 30
val x = 0
inner class Inner {
val x = 1
fun Int.foo() {
assertEquals(1, this@Inner.x)
assertEquals(0, this@Outer.x)
assertEquals(30, this@Outer.foo())
}
}
}
✅ 优点:这种写法能清晰地区分内层与外层类的成员,避免命名冲突。
4.3. 明确访问扩展函数
在嵌套的扩展函数中,使用限定 this
可以访问特定层级的接收者:
class Outer {
inner class Inner {
fun Int.foo() {
fun Int.bar() {
assertEquals(32, this)
assertEquals(42, this@foo)
fun Int.baz() {
assertEquals(20, this)
assertEquals(42, this@foo)
}
20.baz()
}
32.bar()
}
}
}
在这个例子中,this@foo
明确指向最外层的 foo()
扩展函数的接收者(即 42)。
5. 总结
本文我们详细讲解了 Kotlin 中 this
的两种主要使用方式:隐式 this 与 限定 this。
✅ 使用建议:
- 当上下文清晰、不存在歧义时,使用 隐式 this。
- 当存在嵌套类、扩展函数或 lambda 表达式时,建议使用 限定 this(this@ClassName) 来避免歧义,提升代码可读性与安全性。
完整示例代码可以在这里查看:GitHub 仓库。