Kotlin

Kotlinとlambda式とインターフェースとSAM変換

More than 1 year has passed since last update.

Kotlin 1.0 がリリースされましたね。
Kotlin 1.0 リリース: JVMとAndroid向けの実用的(Pragmatic)言語 | JetBrains ブログ

Kotlin初心者が色々いじってたらlambda式がそのまま渡せない場所がいくつかあって、思ったようにいかない事があったのでそのときのメモです。

SAM変換でインターフェイス名を書かなくていいのはJavaのメソッド呼出のときだけ

以下のようにJavaのメソッドを呼び出すときにlambda式がSAM変換されるのはよく紹介されています:

Optional.of("").orElseGet { "" }

ですが変数への代入の場合、以下はコンパイルエラーになります:

val sup: Supplier = { "" }

この場合は以下のように書く必要があります:

val sup: Supplier = Supplier { "" }

インターフェイス名を書く書式は https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions で紹介されています。

KotlinのメソッドにJavaのインターフェイスを渡す場合は型を指定しないSAM変換がかからない

こんなメソッドがあるとします:

fun useJavaInterface(sup: Supplier<String>) = sup.get()

これは以下で呼ぶことができます:

useJavaInterface(Supplier { "" })

以下のように書く事はできません:

useJavaInterface { "" }

以下でもコンパイルエラーになります:

useJavaInterface({ "" })

変数への代入と同じ扱いという事なんでしょう。Javaのメソッド呼出だけが特別扱いされてるようです。

KotlinのインターフェイスにはSAM変換がかからない

以下の一つだけメソッドを持ったインターフェースがあるとします:

interface I {
    fun x(): String
}

このインターフェイスを名前を付けずに実装するには以下のように書く必要があります:

val i: I = object: I {
    override fun x() = ""
}

以下のように書くことはできません:

val i: I = I { "" }

当然以下もコンパイルエラーになります:

val i: I = { "" }

どうしてもlambda式が使いたい場合は以下のようなヘルパーメソッドを書けばそれっぽく使えるようになります:

fun I(i: () -> String) = object: I {
    override fun x(): String = i()
}

val i: I = I { "" }

以下のような事も書いてますし関数型使えばいいじゃんって事なんでしょうが、このくらいは特別扱いしてくれもいいのにとも思います。

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.

おわりに

KotlinかわいいよKotlin。