Open1

Kotlinのレシーバー

sagittariussagittarius

Kotlinのレシーバー(Receiver)

Kotlinの「レシーバー(Receiver)」とは、関数が呼び出される対象となるオブジェクトのことです。
特定のオブジェクトのコンテキスト内で関数を実行することを可能にする概念で、特に拡張関数スコープ関数で重要になります。


レシーバーの種類

1. ディスパッチレシーバー(Dispatch Receiver)

クラスのインスタンスそのものです。
クラスのメンバー関数(メソッド)を呼び出すときに、その関数が属するオブジェクトがディスパッチレシーバーです。

class MyClass {
    fun greet() {
        println("Hello from MyClass!")
    }
}

fun main() {
    val myObject = MyClass()
    myObject.greet() // myObject がディスパッチレシーバー
}

この greet 関数の中で this と書くと、myObject のインスタンスを指します。


2. 拡張レシーバー(Extension Receiver)

拡張関数が拡張する型(クラス)のインスタンスです。
拡張関数は、既存のクラスに新しい関数を追加できますが、その内部では拡張対象のインスタンスをレシーバーとして受け取ります。

fun String.sayHello() {
    println("Hello, $this!") // $this は拡張レシーバー(Stringのインスタンス)
}

fun main() {
    "World".sayHello() // "World" が拡張レシーバー
    val name = "Alice"
    name.sayHello()    // name が拡張レシーバー
}

この sayHello 関数の中で this と書くと、拡張対象の String のインスタンスを指します。


なぜレシーバーが重要なのか?

  • コンテキストの提供
    → 関数がどのオブジェクトのデータや振る舞いを操作するのか、その「文脈」を提供する。

  • コードの可読性と表現力
    → 拡張関数により、既存クラスを修正せずに新しい機能を追加できる。
    → 例: string.isNullOrEmpty()

  • スコープ関数(apply, with, run, also, let など)
    → 特定のオブジェクトのコンテキスト内で複数の操作を行える。冗長なコードを削減し、可読性を向上。

  • DSL(ドメイン固有言語)の構築
    → レシーバー付きラムダ(Lambda with Receiver)により、オブジェクトの内部にいるかのように自然な構文で記述できる。


スコープ関数とレシーバー

スコープ関数は、レシーバーの概念を活用しています。

applywith

ラムダのレシーバーとしてオブジェクト自身を渡す。
ラムダ内で this を使うと、そのオブジェクトを参照する。

data class Person(var name: String, var age: Int)

fun main() {
    val person = Person("Alice", 30).apply {
        // this は Person オブジェクト
        this.age = 31 // this は省略可能
        name = "Alicia"
    }
    println(person) // Person(name=Alicia, age=31)
}

letalso

ラムダの引数としてオブジェクト自身を渡す。
ラムダ内で it を使うと、そのオブジェクトを参照する。

fun main() {
    val numbers = mutableListOf("one", "two", "three")
    val firstAndSecond = numbers.also {
        // it は mutableListOf("one", "two", "three")
        println("The list elements before adding new one: $it")
        it.add("four")
    }
    println(firstAndSecond) // [one, two, three, four]
}