5.8.8 this 表达式

原文链接: https://kotlinlang.org/docs/this-expressions.html

5.8.8 this 表达式

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,请添加标签限定符:

1
this@label

该标签告诉编译器要访问哪个接收者。你可以使用外层类或扩展函数的名称。例如,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()
}