kotlinの無名関数、無名クラス、クロージャーとラムダ式


いつも、あれ?どうやって書くんだっけ?と都度ググっているので整理したことをメモ。

無名関数

基本

宣言と同時に関数オブジェクトに

val func1 = fun(data:Int):Int{ return data }
func1( 1 )
宣言済みメソッドを関数オブジェクトに
fun returnInt(data:Int){ return data }
// KFunction<Int, Int>
val func2 = ::returnInt
//大抵の場合はコールバックに渡すと思うので、型が合うようダウンキャストする
val func3 = ::returnInt as (data:Int)->Int
//または、
val func4:(data:Int)->Int = ::returnInt
引数なしメソッドを関数オブジェクトに

引数なしメソッドの場合はラムダ式で記述できる

val func5:()->Int = { 1 * 1 }

無名クラス

基本
val object1 = object {
    fun foo(){ }
}
object1.foo()
抽象クラスを継承した無名クラス
val object2 = object : BaseAdapter(){
    override fun getCount(): Int { return 0}
    override fun getItemId(position: Int): Long {return position.toLong()}
    override fun getItem(position: Int): Any {return position}
    override fun getView(position: Int, convertView: View?, parent: ViewGroup?): View {return convertView!!}
}
インターフェースを実装した無名クラス
val e = object: View.OnClickListener{
    override fun onClick(v: View?) { }
}

コールバック、クロージャとラムダ

コールバックの定義
val object1 = object {
    var value:Int = 0
    fun foo(){ }
    fun bar(callback:(v:Int)->Int){
        callback(value)
    }
}

与えられるコールバックをnullableにしたい場合は、(v:Int)->Intをnullableで括る

//引数callbackはOptionalになる
fun bar(callback:( (v:Int)->Int )?){
    //アンラップ
    callback?.let { callback -> 
        callback(value)
    }
}
コールバックの呼び出しとクロージャの利用
val i = 2
//その場でクロージャとなる無名関数を定義
object1.bar( fun(v:Int):Int{ return v * i } )
//定義済み関数オブジェクトを渡す(例:上記で定義したfunc3を渡す)
object1.bar( func3 )
//ラムダでクロージャを渡す
object1.bar( { v -> v * i } )
//ラムダの場合、barの引数が1つのみの場合、```()```を省略できる
object1.bar{ v -> v * i }
//barの最後の引数がコールバックの場合、```()```の後、ラムダを記述できる
object1.bar( 1, 2 ){ v -> v * i }
//コールバックの引数が2つ以上の場合はこう記述する
object1.bar{ k, v -> v * i + k }

ラムダ式中でのreturn

基本は最終行の式が評価がreturnされるが、guard return等でラムダ中でreturnしたいことがある。 普通にreturnすると呼び出し元メソッドのスコープをreturnしようとするのでエラーなる。

fun onResume(){
    object1.bar{ v ->
        //'return' is not allowed here
        if( v == 1){ return 1 }
        // valid return
        v * 2
    }
}

ラムダのローカルスコープに対してreturnするには以下のように記述すればよい

object1.bar{ v ->
    if( v == 1){ return@bar 1 }
    v * 2
}

インターフェース実装を受け取るメソッドにラムダの記述

実装メソッドが1つしか必要としないインターフェースを必要とするメソッドにはラムダ式で省略記述した実装を渡すことができる。

Button(this).setOnClickListener{ view ->
    //onClick時の実装を書ける
}

これは下記と等価の実装となる

Button(this).setOnClickListener( object: View.OnClickListener{
    override fun onClick(v: View?) {
    }
} )

ただし、インターフェースのみを無名クラスとしてoverride記述を省略することはできない

//これはダメ
val callback = object: View.OnClickListener{ view ->
}

これは多分、{}がラムダ文法ではなく、無名クラスobject:のクラス定義としての文法として意味をなしている為?