くま's Tech系Blog

基本的には技術で学んだことを書き留めようと思います。雑談もやるかもね!

Kotlinのオブジェクト式について

個人的な意見ですが、オブジェクト式や無名オブジェクトを理解するのに若干苦労したのでまとめようと思います

合わせて、無名関数も苦労したので合わせて記載しようかと思います

オブジェクト式とは?

オブジェクト式では無名オブジェクト(無名クラス)の定義・生成を同時に行います

また、無名オブジェクトはクラスやオブジェクト自体に名前が無いのが特徴です

オブジェクト式を使って生成したオブジェクトは、変数に格納するか、関数に渡すパラメータとして使用します(そうしないと参照できないため)

オブジェクト式の定義方法

objectの後に括弧({})を使い、その中にプロパティやメソッドを定義します

例えば次のような使い方です

val test = object {
    val number = 1
    fun run() = "テスト"
}
println(test.number)  //1
println(test.run())   //テスト

インターフェイスを継承する

オブジェクト式はクラスや1つ以上のインターフェイスを継承できますアプリ開発でよく使う例は、インターフェイスを実装した無名クラスです

interface TestInterface {
    fun showMessage(): String
}
val x = object: TestInterface {
   //インターフェイスのメソッドをオーバーライド
   override fun showMessage(): String {
       return "メッセージ表示"
    }
}

println(x.showMessage())   //メッセージ表示

さらに無名クラスは関数のパラメータ(引数)として、関数に渡すことができます

先程使用したインターフェースを引き続き使用します。 まず関数定義は次です

//インターフェイスを実装したオブジェクトを引数に取る
fun runFunction(o: TestInterface) {
    println(o.showMessage())
}

そして関数を実行するのは次のようになります

runFunction(object: TestInterface {
    override fun showMessage(): String {
        return "スタート"
    }
})

無名関数

無名関数は無名オブジェクトと同じように宣言と同時に関数オブジェクトになるものです。また、関数自体に名前がないです

fun(a:Int, b:Int):Int = a+b

無名関数は値として使用するため変数にセットできます

val addItem = fun(a:Int, b:Int):Int = a+b
println(addItem(3, 6))

ちなみに、ラムダ式も無名関数の一種です

val addLambda = {a:Int, b:Int -> a+b}
println(addLambda(3, 6))

無名関数は高階関数の一種です。最後に高階関数について軽く説明します

高階関数

Kotlinにおいて高階関数は第一級オブジェクトとして扱えます。第1級オブジェクトは関数リテラルとして表現できます

関数リテラルとは、関数を 直接、波括弧 「{ }」 内に記述する記法です。関数リテラル記法で記述すると、コードが即座にそのまま式として渡せます

つまり、関数を引数で受け取ったり、関数を返したりする関数のことを高階関数と呼びます

例えば次のような処理です

// body は () -> T の関数型で、引数なしで T 型の戻り値を返す関数を表している。
fun <T> lock(lock: Lock, body: () -> T): T {
  lock.lock()  // 処理を実行する前にロックする
  try {
    return body()  // 渡された関数を実行
  }
  finally {
    lock.unlock()
  }
}

上記の関数を呼び出す場合には引数に別の関数を渡します

// toBeSynchronized()関数を宣言
fun toSynchronize() = test()

// このtoBeSynchronized()関数を渡したいのだが、それには::で参照を取得する
val result = lock(lock, ::toSynchronize)

もしくは次のようにラムダを渡すこともあります(こちらの方が一般的)

val result = lock(lock, { test() })

// 最後の引数が関数なら、引数を並べる( )括弧の外に出す
lock (lock) {
  test()
}

引数として関数を渡すと、変数名の後に: ()->Stringのように、関数の型を明示します

例えば、Intを引数として受け取り、Stringを返す関数は、(Int)->Stringのように表現します。 ->の左が因子、 ->の右側が戻り値を意味します

そして、引数は括弧で囲む必要があります

もし引数が2つ以上ある場合、 (Int, Int)->Stringように表現することができます。 引数がない場合は、 ()->Stringのように括弧の中を空にします

またここまでの説明をもとにしてよく使うsetOnClickListenerを見ていきます

setOnClickListenerは Viewクラスのメソッドであり、それを継承する任意のクラスで使用できます。 本来であればonClickメソッドをoverrideした状態までをコード化するのですが、次のように省略することもできます

// View.OnClickListenerの単一抽象メソッドはonClickメソッドで、これらはViewを引数にとり戻りなしで共通
button.setOnClickListener(Object: View.OnClickListener {
    override fun onClick(view View?){
        TapAction()
    }
})

↓

// これを高階関数で省略すると
button.setOnClickListener{ view -> TapAction() }

↓

// ラムダのviewパラメータは使用しないので更に省略する
button.setOnClickListener{ TapAction() }

参照

dogwood008.github.io

qiita.com