Kotlin

KotlinでSAM(Single Abstract Method)インターフェースを作ったら省略記法が使えなかった話

問題

JavaでInterface定義し、抽象メソッドが一つだけのもの(Single Abstract Method: SAM)をKotlinで呼び出すときは下記みたいな感じでいける

view.setOnClickListener { view ->
  // something
}

一方、下記のようにKotlinでSAMのInterfaceを作っても同じように呼び出せない

  • 呼ばれる側
interface HogeListener {
    fun fuga(s: String)
}

setHogeListener(listener: HogeListener) {
    this.listener = listener
}
  • 呼ぶ側
hogehoge.setHogeListener { s ->

} 
// ERROR: 
//   Type Mismatch.
//   Required: A.HogeListener
//   Found: (???) -> Unit
//   Cannot infer a type for this parameter. Please specify it explicitly.

原因

https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions

Also note that this feature works only for Java interop; since Kotlin has proper function types, automatic conversion of functions into implementations of Kotlin interfaces is unnecessary and therefore unsupported.

JavaのSAMはよしなに変換してくれるけど、Kotlinは必要ないから変換しないとのこと。

対応

とりあえずJavaの時と使い勝手を変えたくなかったので下記のように実装。

class Hoge(private var listener: (String) -> Unit) {
    fun fuga(string: String) {
        listener(string)
    }
}

setHogeListener(listener: (String)->Unit) {
    listener = Hoge(listener)
}

実際にはKotlin化と同時に不要なインターフェースを無くしていくのがベストなんだろなぁと思いつつ、一時対応としてメモ。