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 实例、someNumbersomeObject 和扩展函数中的 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 仓库


原始标题:Qualified this and Implicit this in Kotlin