Genta Hirauchi

公開日:2020/01/31
更新日:2020/03/22

【Kotlin基礎】スコープ関数applyの実装方法と用途を解説

  • applyの実装方法が知りたい。
  • applyはどんな時に使うと便利なの?
  • apply以外のスコープ関数について知りたい。

apply、みなさん使っていますか?

スコープ関数は、初めはなかなか理解しづらいかもしれませんが、覚えておくととても便利です。

そこで本記事では、スコープ関数applyについて解説致します。スコープ関数はいくつかありますが、個人的に最も使用頻度の高い関数です。

他のスコープ関数につきましては、簡単な解説と、詳細をまとめた記事へのリンクがありますので、そちらをご覧頂けたらと思います。

目次

スコープ関数applyの実装方法と用途について

applyの基本的な使い方

スコープ関数applyは、公式ドキュメントで以下のように定義されております。

  • Kotlin
inline fun  T.apply(block: T.() -> Unit): T

【公式ドキュメント】 : apply – Kotlin Programming Language

公式ドキュメントの説明文には、「Calls the specified function block with this value as its receiver and returns this value.(この値をレシーバーとして、指定された関数ブロックを呼び出し、この値を返します。)」と記されています。

もう少しイメージしやすい形にすると、以下のようになります。

  • Kotlin
結果 = レシーバー.apply {
   // 関数ブロック
}

「レシーバー」の部分には、オブジェクトが入ります。
このレシーバーに対し、ブロック{ }内で処理を行い、最後にレシーバー自身を返すというのがapplyの基本的な使い方です。

では、実装例で詳しく見ていきましょう。

  • Kotlin
class User {
   var name: String = ""
   var age: Int = 0
}

val user = User()

val result = user.apply {
   this.name = "田中"
   this.age = 20
}

println("私の名前は${result.name}です、${result.age}才です。")

// "私の名前は田中です、20才です。

Userクラスのインスタンスuserをレシーバーとしてapplyを呼んでいます。

applyのブロック{ }内のthisは、レシーバーであるuserを指しております。
このthisは、省略することも可能です。

ブロック内では、userのパラメータに値を代入しております。

applyは、ブロック処理後にレシーバー自身を返すので、変数resultには、パラメータに値が代入されたUserが格納されることになります。

先ほどのサンプルコードは、説明のために少し丁寧に実装しておりますが、実際はもっと簡潔に実装することもできます。

  • Kotlin
class User {
   var name: String = ""
   var age: Int = 0
}

val result = User().apply {
   name = "田中"
   age = 20
}

println("私の名前は${result.name}です、${result.age}才です。")

// "私の名前は田中です、20才です。

どうでしょう?applyの魅力に気付き始めたのではないでしょうか。

次節では、よりapplyの魅力を知ってもらうため、僕自身が実際に使用する場面を、いくつか紹介したいと思います。

Point
  • applyを呼び出すオブジェクトをレシーバーと呼ぶ
  • ブロック処理内では、レシーバーをthisで呼び出せる。また、thisは省略できる
  • applyはブロック処理後、レシーバーを返す

applyの具体的な使用用途

続いて、僕自身が実際に使用している、applyの使用例をいくつか紹介したいと思います。

■ コードで動的にViewを実装する

  • apply使用
  • apply未使用
val container = findViewById(R.id.container)
container.apply {
   orientation = LinearLayout.VERTICAL

   val textView = TextView(this@MainActivity).apply {
       text = "テキストが入ります。"
       textSize = 18f
       textAlignment = TextView.TEXT_ALIGNMENT_CENTER
   }

   val button = Button(this@MainActivity).apply {
       setText("ボタン")
       layoutParams = LinearLayout.LayoutParams(200, 100)
       setOnClickListener {
           Toast.makeText(this@MainActivity, "トースト", Toast.LENGTH_SHORT).show()
       }
   }

   addView(textView)
   addView(button)
}
val container = findViewById(R.id.container)
container.orientation = LinearLayout.VERTICAL

val textView = TextView(this)
textView.text = "テキストが入ります。"
textView.textSize = 18f
textView.textAlignment = TextView.TEXT_ALIGNMENT_CENTER

val button = Button(this)
button.setText("ボタン")
button.layoutParams = LinearLayout.LayoutParams(200, 100)
button.setOnClickListener {
   Toast.makeText(this@MainActivity, "トースト", Toast.LENGTH_SHORT).show()
}

container.addView(textView)
container.addView(button)

まずはViewをコードで実装する例です。
コードの行数としてはapplyを使用しないほうが少ないですが、applyを使用した実装の方は、各Viewのまとまりや親子関係がわかりやすいと思います。

■ Fragmentのインスタンス生成

  • apply使用
  • apply未使用
fun newInstance(name: String, age: Int, isMen: Boolean, friends: ArrayList): MainFragment {
   return MainFragment().apply {
       arguments = Bundle().apply {
           putString("key_name", name)
           putInt("key_age", age)
           putBoolean("key_isMen", isMen)
           putStringArrayList("key_friends", friends)
       }
   }
}
fun newInstance(name: String, age: Int, isMen: Boolean, friends: ArrayList): MainFragment {
   val bundle = Bundle()
   bundle.putString("key_name", name)
   bundle.putInt("key_age", age)
   bundle.putBoolean("key_isMen", isMen)
   bundle.putStringArrayList("key_friends", friends)

   val mainFragment = MainFragment()
   mainFragment.arguments = bundle

   return mainFragment
}

続いての例は、Fragmentのインスタンスの生成です。

Fragmentに対し、値を受け渡したい場合は、Bundleに詰めて受け渡すと思いますが、applyを使用することですっきりした実装になります。

また、値を受け取る時は、

  • Kotlin
arguments?.apply {
   name = getString("key_name")
   age = getInt("key_age")
   isMen = getBoolean("key_isMen")
   friends = getStringArrayList("key_friends").first()
}

のようにして受け取ることができます。

ここで注目していただきたいのは、argumentsの後の「 ?. 」です。

?. は、セーフコール演算子と呼ばれるもので、オブジェクトがnullでなければ、続く処理を実行してくれます。

これをapplyとセットで使用することで、レシーバーのnullチェックをした上で、レシーバーにアクセスする処理を安全に実行することができます。

Point
  • applyは、オブジェクトにまとめて処理を行う際に便利
  • ?. をセーフコール演算子と呼ぶ
  • ?.apply { }で、nullでないことを確認し、安全に処理を行うことができる

apply以外のスコープ関数について

apply以外のスコープ関数について簡単な解説と、詳しい内容をまとめた記事へのリンクを紹介致します。

runについて

runは、公式ドキュメントで以下のように定義されております。

  • Kotlin
inline fun  run(block: () -> R): R

inline fun  T.run(block: T.() -> R): R

【公式ドキュメント】 : run – Kotlin Programming Language

【使用例】

  • Kotlin
val number = 10
val result = number.run {
   this + 10
}
println(result)

// 20
Point
  • ブロック処理内では、レシーバーをthisで参照できる
  • ブロック処理の最後の値を返す(レシーバーでなくてもよい)

詳しい解説はこちらをご覧ください。

【Kotlin基礎】スコープ関数runの実装方法と用途を解説

Kotlinには、スコープ関数という、シンプルですが、覚えておくと便利な関数があります。スコープ関数にはいくつか種類があるのですが、本記事では、runというスコープ関数について解説致します。

letについて

letは、公式ドキュメントで以下のように定義されております。

  • Kotlin
inline fun  T.let(block: (T) -> R): R

【公式ドキュメント】 : let – Kotlin Programming Language

【使用例】

  • Kotlin
val str = "hello"
val result = str.let {
   it.length
}

println(result)

// 5
Point
  • ブロック処理内では、レシーバーをitで参照できる
  • ブロック処理の最後の値を返す(レシーバーでなくてもよい)

詳しい解説はこちらをご覧ください。

【Kotlin基礎】スコープ関数letの実装方法と用途を解説

本記事では、スコープ関数letについて詳しく解説しております。スコープ関数にはいくつか種類があるのですが、あえてletにのみ焦点を当てる事に致しました。他のスコープ関数(run, apply, also等)につきましては、簡単な解説と、詳細をまとめた記事へのリンクがありますので、そちらをご覧頂けたらと思います。

alsoについて

alsoは、公式ドキュメントで以下のように定義されております。

  • Kotlin
inline fun  T.also(block: (T) -> Unit): T

【公式ドキュメント】 : also – Kotlin Programming Language

【使用例】

  • Kotlin
class User {
   var name: String = ""
   var age: Int = 0
}

val result = User().also {
   it.name = "伊藤"
   it.age = 30
}

println("私は${result.name}、${result.age}です。)

// "私は伊藤、30才です。
Point
  • ブロック処理内では、レシーバーをitで参照できる
  • レシーバー自身を返す

詳しい解説はこちらをご覧ください。

【Kotlin基礎】スコープ関数alsoの実装方法と用途を解説

Kotlinにはスコープ関数という覚えておくと便利な関数があります。スコープ関数にはいくつか種類があるのですが、本記事では、スコープ関数alsoに焦点を当て解説致します。他のスコープ関数(let, run, apply等)につきましては、簡単な解説と、各記事へのリンクがありますので、そちらをご覧頂けたらと思います。

まとめ

  • applyを呼び出すオブジェクトをレシーバーと呼ぶ
  • ブロック処理内では、レシーバーをthisで呼び出せる。また、thisは省略できる
  • applyはブロック処理後、レシーバーを返す
  • applyは、オブジェクトにまとめて処理を行う際に便利
  • ?. をセーフコール演算子と呼ぶ
  • ?.apply { }で、nullでないことを確認し、安全に処理を行うことができる