109
81

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

KotlinのRun, Let, Apply, Alsoを使い分け

Last updated at Posted at 2018-06-05

※ あくまでも自分の見解です。

概要

最近の悩みはrun, let, apply, alsoをどうやって使い分けです。
基本の機能は簡単に分けれます。run, letは戻り値を期待してない、apply, alsoは同じ型を望んでます。
(この辺の使い方は他に沢山の記事がありますので、割愛させてくださいませ :bow:

問題は、じゃあ、「runとlet」と「applyとalso」の差は何でしょうか。
頑張れば全部「let」で実装できるのに、「thisとit」を分ける意味がわかりません!

公式ページの推奨

先ずは公式ページを見てみましょう。

Are you calling methods on multiple objects in the block, or passing the instance of the context object as an argument? If you are, use one of the functions that allows you to access the context object as it, not this (also or let).

つまり、引く数として他の値に渡すかどうかです。

例えば下の例なら、「it」を使うのが推奨です。
このScopeの中で「it」は変化はしません、一貫性を持ちます。


// Context object is 'it'
class Baz {
    var currentBar: Bar?
    val observable: Observable

    val foo = createBar().also {
        currentBar = it                    // Accessing property of Baz
        observable.registerCallback(it)    // Passing context object as argument
    }
}

また、自分自身に対する変更は「this」を使います。
なので、基本的に「this」が出る時は書き方がよくないと思っても良いでしょう。


// Context object is 'this'
class Baz {
    val foo: Bar = createBar().apply {
        color = RED    // Accessing only properties of Bar
        text = "Foo"
    }
}

ニュアンスから想像しましょう

使い方が分かりましたけど、実際にCodeを書くときは忘れてしまいそう。
なので、想像して納得しましょう。

以下は私の考えです、一応参考まで。

Run

「私が実行します!」と自分自身でいろいろやります。
そして、その結果は誰も望んでません。

例:Toolbarを設定する


var toolbar = findViewById<Toolbar>(R.id.toolbar)
toolbar.setNavigationIcon(R.drawable.icon)
toolbar.setNavigationOnClickListener(view -> doSomething())

//run
findViewById<Toolbar>(R.id.toolbar)
  .run {
    setNavigationIcon(R.drawable.icon)
    setNavigationOnClickListener({ doSomething() })
  }

同じObjectが何回も実行する時に、ちょっとだけ便利になるくらいです。
あまり役に立たないかもしれません。:frowning2:

Let

「Let it be」のような感じで、この子を他のところに行かせよう。
「自分から他人に」、自身は何も変化しません。

例:Intentを渡す前にnull check


val intent = getIntent()
if (intent != null) {
  setIntent(intent)
}

//let
getIntent()?.let { setIntent(it) }

定番の引数を渡すCaseです。
何かを渡したい時は「let」を使えば良いと思います。

Apply

「私に応用する」、自分自身に何かを反映する。
自分を渡す前にもう一回自分を強化するイメージです。

例:FragmentのnewInstance


fun newInstance(bundle: Bundle): FooFragment{
  var fragment = FooFragment()
  fragment.setArguments(bundle)
  return fragment
}

//apply
fun newInstance(bundle: Bundle): FooFragment{
  return FooFragment().apply {
    setArguments(bundle)
  }
}

同じ定番のBundleを設定するCaseです。
何かを返却する前に、Objectの値をいじりたい時に使いましょう。

Also

「ついでに」です。この値を渡す前に、ついでに他の値にも渡します。
何かを提出前に、他の人にもチラ見せします。 |д゚)

例:getInstanceの返却


fun getInstance(context: Context): FooDatabase {
  if (INSTANCE != null) {
    return INSTANCE
  } else{
    INSTANCE = buildDatabase(context)
    return INSTANCE
  }
}

//also
fun getInstance(context: Context): FooDatabase {
  if (INSTANCE != null){
    return INSTANCE
  } else{
    return buildDatabase(context).also { INSTANCE = it }
  }
}

//もっと省略
fun getInstance(context: Context): FooDatabase {
  return INSTANCE ?: buildDatabase(context).also { INSTANCE = it } |
}

他の子にも見せたい時に使いましょう。
公式によると、特に「it」を使いたくない時にも使えまう。
例えば LOG.info("Something created")とかのLog系。

他のかたの考え

また、こちらの考えも面白いです。
Example of when should we use run, let, apply, also and with on Kotlin (stackoverflow)

複雑になった場合

複雑になった時は無理に一個のScope Functionで書かなくても良いです。
Chainができますので、複数個で解決しましょう。

getIntent()
  .let { intent ->
   sendIntent(intent.setData(data)) //itを弄らない
  }

getIntent()
  .apply {
    setData(fooData)
  }.let {
    sendIntent(it)
  }

終わり

Codeは動かせるだけではなくで、感じるんだ
一緒に綺麗なCodeを書きましょう :hushed:

参考

更新:例を追加しました。(06/14)

109
81
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
109
81

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?