Kotlinの関数参照 #Kotlin_Sansan

  • 22
    Like
  • 0
    Comment

はじめに

この記事は第5回Kotlin勉強会@Sansanの発表で使用した内容です。


自己紹介


もくじ

  1. 関数参照
  2. レシーバが未定のメソッド参照
  3. レシーバが確定のメソッド参照
  4. プロパティも関数

1. 関数参照


関数オブジェクト

  • Kotlinでは定義済みの関数のオブジェクトを得ることができる
  • 関数オブジェクトを変数に代入したり、別の関数の引数に渡したり、持ち回して便利に使える。
  • 必ずしもオブジェクトではない(ヒント: インライン関数)

コード例

// 普通の関数
fun succ(n: Int): Int = n + 1

// 関数オブジェクトを取得し、変数に代入
val myFunc: (Int)->Int = ::succ
//          ^^^^^^^^^^ 関数型

// メソッド呼び出しによる元の関数の計算を実行
myFunc.invoke(5) //=> 6

// 演算子オーバロードによる関数らしい記述
myFunc(7) //=> 8

応用例

// 高階関数の引数に「関数参照」を渡す
listOf(1, 2, 3).map(::succ) //=> [2, 3, 4]

// ラムダ式を使えば同じ
listOf(1, 2, 3).map { succ(it) } //=> [2, 3, 4]
  • ラムダ式は、文を手続き的に記述していく
  • 関数参照は、宣言的な記述になる

2. レシーバが未定のメソッド参照


メソッド参照

  • メソッドのオブジェクトも取得できる
  • この場合は、レシーバが未定
// 「型::メソッド名」という書式で取得
val foo: String.()->String = String::reversed
//       ^^^^^^^^^^^^^^^^^ 関数型

// メソッドっぽく呼び出せる
"Kotlin".foo() //=> niltoK

拡張関数もOK

fun String.bold(): String = "**$this**"
val baz: String.()->String = String::bold
"Sansan".baz() //=> **Sansan**

普通の関数型にもなるよ

  • A.(B)->Cという型は、(A, B)->Cと同じっぽい
fun String.bold(): String = "**$this**"

// 型どっちでもOK
val aaa: String.()->String = String::bold
val bbb: (String)->String = aaa

// 呼び出し方どっちでもOK
"hoge".aaa() //=> **hoge**
aaa("fuga") //=> **fuga**

高階関数で効果を発揮

"Hello".let { it.bold() }
"Hello".let(String::bold)

もしString.()->String(String)->Stringとみなせなかったら、2行目の書き方がNGです。


3. レシーバが確定のメソッド参照


Bound Function Reference

  • bound = 束縛された
  • → レシーバが確定してるメソッド参照
  • Kotlin 1.1から使用できる

コード例

// 「オブジェクト::メソッド名」の書式で取得
val succ: (Int)->Int = 1::plus
succ(5) //=> 6
userRepository.findById(123L)
  ?.let { user -> user.copy(blocked = true) }
  ?.let(userRepository::update)

4. プロパティも関数


プロパティも関数として扱える

val prop: KProperty1<String, Int> = String::length
prop.get("Taro") //=> 4

val func: (String) -> Int = prop
func("Taro") //=> 4

高階関数で効果を発揮

listOf("foo", "wa").map { it.length }   // => [3, 2]
listOf("foo", "wa").map(String::length) // => [3, 2]

もしKProperty1<String, Int>(String)->Stringとみなせなかったら、2行目の書き方がNGです。


まとめ

  • 関数オブジェクトを使うと宣言的な記述ができる
  • ::hoge: 関数参照
  • Foo::bar: メソッド参照
  • foo::bar: レシーバ固定メソッド参照 (Bound Function Reference)
  • プロパティも関数オブジェクトになれる
  • 次に見るべきスライド?(ネタ枠)