Kotlinには、処理のブロック内({ }のこと)でスコープを変えることによって、簡潔な記述が出来る便利なスコープ関数が、標準関数として用意されています。
ただ、便利な半面ちょいややこしいという面もあります。
そこで、よく使うと思われる「apply と also」を、本当にざっくり過ぎる感じで解説してみました。
もくじ
本当にざっくり過ぎるまとめはこれ
スコープ関数はそもそも標準関数なので、使いみちは色々考えられるかと思うのですが、本当にざっくり言うと、「apply」と「also」はオブジェクトの(複雑な)初期化や設定をするときに使えると思っておけばいいのではないかと。
違いは「this」と「it」
関数の定義で見てみると
applyはこれ
/** * Calls the specified function [block] with `this` value as its receiver and returns `this` value. */ @kotlin.internal.InlineOnly public inline fun <T> T.apply(block: T.() -> Unit): T { ← applyは処理対象Tをレシーバーとして受け取っている contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) } block() return this }
alsoはこれ
/** * Calls the specified function [block] with `this` value as its argument and returns `this` value. */ @kotlin.internal.InlineOnly @SinceKotlin("1.1") public inline fun <T> T.also(block: (T) -> Unit): T { ← alsoは処理対象をラムダ式の引数(it)として受け取っている contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) } block(this) return this }
どちらも、「this」をブロック内で処理してその結果を返すという点では同じですが、
「apply」の場合は処理対象をレシーバーとして受け取り、
「also」の場合はラムダ式の引数(it)として受け取る点が違うようです。
「also」は「apply」より後に産まれたらしいのですが、ブロック内外で「this」が変わってしまう「apply」の代替として考え出されたのでしょうか。
例はこんな感じ
なので、実際に例を見てみると、違いは処理対象の受け取り方(呼び出し元へのアクセスの仕方)だけです。
もともとこんなコードは
val intent = Intent(this@WordListActivity, EditActivity::class.java) intent.putExtra(getString(R.string.intent_key_question), strSelectedQuestion) intent.putExtra(getString(R.string.intent_key_answer), strSelectedAnswer)
こんな感じになります。
applyの方は、処理対象である「Intent」をレシーバーの「this」としてアクセスしており、この「this」は省略できるので「putExtra」の前には何もいらなくなる。その代わりブロック内外で「this」の意味合いが変わってしまう。
一方、alsoの方は純粋なラムダ式なので、処理対象である「Intent」はその唯一の引数「it」として受け取っているため、「putExtra」の前に「it」が必要になる。その代わりブロック内外で「this」の意味合いは同じまま。
とはいえ、Kotlinを作った天才の方々にはいろんな思惑があるかと思いますが、apply・alsoとも結局は処理対象の自分自身を返すわけなので、そうであれば、オブジェクトの初期化に関しては、より簡潔に記述できる「apply」で良いのではないかと、凡人の私は思ったりするわけであります。
(詳しくは下の動画で解説していますので、よろしければご覧ください)