apply を使う意味と実装方法|スコープ関数|Kotlin|開発裏話

スコープ関数 apply は使い慣れると本当に便利で、スレッド式メモ帳アプリ『CBnotes』の実装でも多用しています。

スコープ関数 apply はとにかく至る所で使いたくなりますが、初見だとチンプンカンプンなのは確かです。

以下は『CBnotes』に存在する実装です。ダイアログにパラメータを渡して表示する処理です。ここで apply を使っています。

/**
 * Display the dialog.
 */
private fun showLabelNameDialog() {
    // Calls the specified function [block] with `this` value as its receiver and returns `this` value.
    LabelNameDialogFragment().apply {
        // Supply the construction arguments for this fragment.
        arguments = Bundle().apply {
            // Inserts a String value into the mapping of this Bundle, replacing
            // any existing value for the given key.
            putString(EXTRA_LABEL_NAME, getLabelFragment()?.getLabelName() ?: "")
        }

        // Display the dialog, adding the fragment to the given FragmentManager.
        show(supportFragmentManager, LabelNameDialogFragment.TAG)
    }
}

画像1

先ずは、少しずつでも情報を得て、apply について知っていくことが大切です。

公式ソースコード(Standard.kt)

早速、apply の実装や構造がどうなっているのか、公式のソースコードを読んでみましょう。

Standard.kt

/**
 * Calls the specified function [block] with `this` value as its receiver and returns `this` value.
 *
 * For detailed usage information see the documentation for [scope functions](https://kotlinlang.org/docs/reference/scope-functions.html#apply).
 */
@kotlin.internal.InlineOnly
public inline fun <T> T.apply(block: T.() -> Unit): T {
    contract {
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    block()
    return this
}

コメントを翻訳してみます。

Calls the specified function [block] with `this` value as its receiver and returns `this` value.
`this` 値をレシーバとして指定された関数ブロックを呼び出し、` this` 値を返します。

For detailed usage information see the documentation for [scope functions](https://kotlinlang.org/docs/reference/scope-functions.html#apply).
使用法の詳細については「スコープ関数」(https://kotlinlang.org/docs/reference/scope-functions.html#apply)のドキュメントを参照してください。

`this` 値を関数ブロック内で使用し、その使用した `this` 値を返せるとのこと……素直に「スコープ関数」のドキュメントを参照してみましょう。

「スコープ関数」のドキュメント

以下、翻訳して読んでみます。

The context object is available as a receiver (this). The return value is the object itself.
コンテキストオブジェクトは、レシーバ(this)として使用できます。 戻り値はオブジェクト自体です。

これ↑は先程のソースコードのコメントと同じです。以下はどうでしょうか。

Use apply for code blocks that don't return a value and mainly operate on the members of the receiver object. The common case for apply is the object configuration. Such calls can be read as “apply the following assignments to the object.”
値を返さず、主にレシーバオブジェクトのメンバーを操作するコードブロックには、apply を使用します。適用の一般的なケースは、オブジェクト構成です。このような呼び出しは、「オブジェクトに次の割り当てを適用する」と読むことができます。
Having the receiver as the return value, you can easily include apply into call chains for more complex processing.
レシーバを戻り値として使用すると、適用を呼び出しチェーンに簡単に組み込んで、より複雑な処理を実行できます。

値を返さなくても、オブジェクトに何らかを「適用(apply)」し、オブジェクトを構成したい場合には apply を使用する、値を返せばそれはチェーンに組み込めるので、便利ですよ、ということだそうです。

何となく朧げながらも分かってきたような気がしませんか。

CBnotes』における実装

では、改めて『CBnotes』の実装を見てみましょう。

/**
 * Display the dialog.
 */
private fun showLabelNameDialog() {
    // Calls the specified function [block] with `this` value as its receiver and returns `this` value.
    LabelNameDialogFragment().apply {
        // Supply the construction arguments for this fragment.
        arguments = Bundle().apply {
            // Inserts a String value into the mapping of this Bundle, replacing
            // any existing value for the given key.
            putString(EXTRA_LABEL_NAME, getLabelFragment()?.getLabelName() ?: "")
        }

        // Display the dialog, adding the fragment to the given FragmentManager.
        show(supportFragmentManager, LabelNameDialogFragment.TAG)
    }
}
● LabelNameDialogFragment() に arguments を適用している(値=LabelNameDialogFragment は返さない)
Bundle() に putString を適用している(適用済みの値=Bundle を返す)

値を返す場合も、返さない場合も、初期状態のオブジェクトに「apply(適用)」して、オブジェクトを構成しています。そして、構成したオブジェクトを返してそのまま arguments に設定できているところが、apply の便利ポイントです。

Android で使用頻度の高い汎用ダイアログ(DialogFragment)。このダイアログを表示する処理は、apply の使用箇所として、最適ではないでしょうか。

CBnotes』ソースコード一式

以下 note で、実際に Google Play へ公開リリースしている『CBnotes』のソースコード一式を販売しております。

Android(Kotlin)アプリ開発を学習している方々には「参考教材」として、業務で動作実績のあるサンプルコード&開発環境一式が必要なプロの方々には「工数削減」として、大変にオススメです。


この記事が気に入ったらサポートをしてみませんか?