this 式
this 式は、現在のレシーバー(その関数が動作しているオブジェクト)を指します。this をどのように使用するかは、コンテキストによって異なります。
クラスのメンバー内では、
thisはそのクラスの現在のオブジェクトを指します。例えば、次のコードでは、
this.nameは現在のLanguageオブジェクトのnameプロパティを意味します。kotlinclass Language(val name: String) { fun printName() { println(this.name) } } fun main() { val language = Language("Kotlin") language.printName() // Kotlin }拡張関数またはレシーバー付き関数リテラル内では、
thisはレシーバーを指します。次の例では、
lastCharacter()が文字列"Kotlin"に対して呼び出されています。したがって、"Kotlin"がレシーバーです。拡張関数内では、thisは"Kotlin"を指します。これは、this.lengthが"Kotlin"の長さであることを意味します。kotlinfun String.lastCharacter(): Char { println(this.length) // 6 return this[this.length - 1] } fun main() { println("Kotlin".lastCharacter()) // n }
シンプルなケースでは、
thisを明示的に記述する必要はありません。Kotlin は現在のスコープからそれを解決します。 詳細については、暗黙の this をご覧ください。
修飾付きの this (Qualified this)
レシーバースコープがネストされている場合、コード内で複数のレシーバーを同時に利用できることがあります。Kotlin は利用可能な任意のレシーバーを使用してそのメンバーに暗黙的にアクセスできますが、内側のスコープのレシーバーがより高い優先度を持ちます。特定のレシーバーを明示的に参照するには、修飾付きの this を使用します。これは、複数のレシーバーが同じ名前のメンバーを持っており、外側のレシーバーのメンバーにアクセスする必要がある場合に特に便利です。
修飾付きの this を使用するには、ラベル修飾子を追加します。
this@labelラベルは、どのレシーバーにアクセスするかをコンパイラに伝えます。囲んでいるクラスや拡張関数の名前を使用できます。例えば、this@foo は、foo という名前の囲んでいる拡張関数のレシーバーを指します。
インナークラスから外側のクラスにアクセスする
インナークラスでは、修飾子のない this はインナークラスのインスタンスを指します。外側のクラスのオブジェクトにアクセスするには、修飾付きの this を使用します。
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()
}インナークラスのみが外側のクラスのインスタンスへの参照を保持します。通常のネストしたクラス(nested classes)は、外側のクラスの
thisにアクセスできません。
拡張関数からクラスにアクセスする
クラス内で拡張関数を宣言すると、2 つのレシーバーが利用可能になります。
- 拡張レシーバー(extension receiver):拡張関数を呼び出す対象の値。
- ディスパッチレシーバー(dispatch receiver):拡張関数を宣言しているクラスの現在のオブジェクト。
どちらのレシーバーが必要かを指定するには、修飾付きの this を使用します。
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プロパティにアクセスします。
ラムダからのアクセス
通常のラムダとは異なり、レシーバー付きラムダはそのスコープにレシーバーを導入します。その結果、ラムダ内の this は、囲んでいるスコープのレシーバーではなく、ラムダのレシーバーを指します。レシーバー付きラムダが別のレシーバースコープ内にネストされている場合は、ラムダにラベルを追加し、修飾付きの this を使用して、ラムダのレシーバーまたは囲んでいるスコープからのレシーバーを明示的に参照します。
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@はラムダにラベルを付けています。this@stringLabelはラムダの文字列レシーバーを指します。this@Userは現在のUserオブジェクトを指します。
ラベルは何かを呼び出したり、ラムダの動作を変更したりするものではありません。ラムダのレシーバーを参照するのに役立つだけです。
匿名オブジェクトまたはその外側のクラスへのアクセス
匿名オブジェクトには独自のレシーバースコープがあります。オブジェクト本体内では、修飾子のない this は匿名オブジェクト自体を指します。ただし、匿名オブジェクトにはクラス名がないため、this の修飾子として使用することはできません。したがって、匿名オブジェクトを参照するには、修飾子のない this しか使用できません。
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 を使用して、囲んでいるオブジェクトにアクセスできます。
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 (Implicit this)
this に対してメンバー関数を呼び出す場合、this. 修飾子を省略できます。ただし、より近いレキシカルスコープ(lexical scope)に同じ名前の別の呼び出し可能なもの(callable)がある場合、Kotlin は修飾されていない呼び出しを、メンバー関数ではなくその呼び出し可能なものとして解決します。明示的にメンバー関数を呼び出すには、this. 修飾子を使用します。
fun main() {
class A {
fun printLine() {
println("Member function")
}
fun invokePrintLine() {
fun printLine() {
println("Local function")
}
printLine()
// Local function
this.printLine()
// Member function
}
}
A().invokePrintLine()
}