Help us understand the problem. What is going on with this article?

Java開発者に送るKotlinのスコープ関数

スコープ関数とは

Kotlinの標準ライブラリに定義されているジェネリック関数。
ジェネリック関数なので、どんな型にも適用できる関数です。
スコープ関数を使うことで、よりKotlinらしいコードが書けます。

letとrun

Streamのmap関数のようなイメージです。

val str: String = "5963"

// 変数strをIntに変換する
// letを使った場合
val i: Int = str.let { it.toInt() }

// 変数strをIntに変換する
// runを使った場合
val i2: Int = str.run { this.toInt() }

letに続くラムダ式は引数が1つです。この引数はstrへの参照です。
runに続くラムダ式は引数がありません。代わりに、このラムダ式内のthis参照はstrです。
ラムダ式の結果がletrunの戻り値となります。

また、今回の例の変数strのような、関数の呼び出されるオブジェクトをレイシーバーオブジェクトといいます。

使いどころ

fun getName(): String? = ""
fun makeGreeting(name: String) = "Hello, $name"

fun greet(): String? {
    val name = getName()
    if (name == null) {
        return null
    }

    return makeGreeting(name)
}

greet()関数では
getName()の結果を、makeGreeting()関数に渡し、
その結果がgreet()関数の戻り値となります。

ただし、getName()の戻り値はnull許容型なので、一度nullチェックが必要です。
これを、letを使って書き換えてみます。

fun greet(): String? {
    return getName()?.let { makeGreeting(it) }
}

getName()の結果を安全呼び出しを使い、letを呼び出します。
こうすると、かなりスッキリ書けます。
安全呼び出しや、安全キャストの結果を関数の引数に渡す場合に便利です。
(もちろん、それ以外の場合にもよく使われます。)

applyとalso

letとrunに似ていますが、戻り値は常にレシーバーオブジェクトです。

val str: String = "5963"

// 変数strを標準出力
// alsoを使った場合
val str2: String = str.also { print(it) }

// 変数strを標準出力
// applyを使った場合
val str3: String = str.apply { print(this) }

使いどころ

class Person {
    var firstName: String? = null
    var middleName: String? = null
    var lastName: String? = null
}

fun registerNewPerson() : Person {
    val person = Person()
    person.firstName = "久美子"
    person.middleName = "スーザン"
    person.lastName = "山本"
    return person
}

registerNewPerson関数では、Personのインスタンスを新たに作って返します。
Personを初期化するだけですが、一旦変数に置かなければなりません。

alsoを使ってみましょう。

fun registerNewPerson() : Person {
    return Person().also {
        it.firstName = "久美子"
        it.middleName = "スーザン"
        it.lastName = "山本"
    }
}

Personの一時変数を作らずに実装できます。
Builderパターンでは無いオブジェクトを扱う場合などに、
そのオブジェクトの初期化処理がスコープ関数の中にまとまるので、可読性向上につながります。

with

withはちょっと変わりダネです。
レシーバーオブジェクトを引数で渡すrun っといったところです。

val str = "5963"
val i: Int = with(str) { this.toInt() }

使いどころ

class Greeter {
    fun greet(): String = "Hello, world"
}

fun greet(greeter: Greeter) = with(greeter) { 
    greet()
}

withを使って単一式関数にします。
this参照が変わり違った文脈で記述できるようになります。

Why do not you register as a user and use Qiita more conveniently?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away