高階関数やラムダ式でnullableの関数型を渡したい場合があります。
..と文章にすると複雑ですが、Buttonで onClick()
コールバックを渡すようないつもの処理の場合です。
nonnullで onClick
定義する場合は以下のように onClick()
を呼ぶと実行できます。
fun nonnullFunction(onClick: () -> Unit) {
onClick()
}
nullableの式を実行する場合、 onClick()
とするとコンパイルエラーとなります。
エラー時のKotlin Playgroundのリンクを以下に貼ります。
https://pl.kotl.in/b3Y5BDPzs?theme=darcula
fun nullableFunction(onClick: (() -> Unit)?) {
onClick() //コンパイルエラー
}
エラー内容は以下の通りです。?.invoke()
を使用するように指示されます。
Reference has a nullable type '(() -> Unit)?', use explicit '?.invoke()' to make a function-like call instead.
指定通り以下のようにすると正しく実行できます。
fun nullableFunction(onClick: (() -> Unit)?) {
onClick?.invoke()
}
invoke()とは
nullableの際に突然出てきた invoke()
が気になり調べました。
下記のページから、
最初のonClick()
はシンタックスシュガーで onClick.invoke()
をオーバーロードしていることがわかります。
Higher-Order Functions and Lambdas
https://kotlinlang.org/docs/reference/lambdas.html
Operator overloading > Invoke operator
https://kotlinlang.org/docs/reference/operator-overloading.html#invoke
すなわち以下は onClick()
を呼んだときの処理と同じ挙動です。
fun nonnullFunction(onClick: () -> Unit) {
onClick.invoke() // equals onClick()
}
kotlin.jvm.functionsのソースコードを見ると invoke()
のインターフェイス定義を見ることができます。
Kotlin1.3では引数定義は0から22まで可能なことがわかります。
package kotlin.jvm.functions
/** A function that takes 0 arguments. */
public interface Function0<out R> : Function<R> {
/** Invokes the function. */
public operator fun invoke(): R
}
:
/** A function that takes 22 arguments. */
public interface Function22<in P1, in P2, in P3, in P4, in P5, in P6, in P7, in P8, in P9, in P10, in P11, in P12, in P13, in P14, in P15, in P16, in P17, in P18, in P19, in P20, in P21, in P22, out R> : Function<R> {
/** Invokes the function with the specified arguments. */
public operator fun invoke(p1: P1, p2: P2, p3: P3, p4: P4, p5: P5, p6: P6, p7: P7, p8: P8, p9: P9, p10: P10, p11: P11, p12: P12, p13: P13, p14: P14, p15: P15, p16: P16, p17: P17, p18: P18, p19: P19, p20: P20, p21: P21, p22: P22): R
}
invoke() の処理を変更
invoke()
をオーバーロードすることにより、関数型を実行するときの処理を変更可能です。
上記Stackoverflowの例がわかりやすかったので参考にしました。
サンプルコードは以下となります。
関数型と invoke()
との関係を知った後だと理解しやすいと思います。
class Greeter(val greeting: String) {
operator fun invoke(target: String) = println("$greeting $target!")
}
fun main() {
val hello = Greeter("Hello")
hello("world") // Prints "Hello world!"
}
Kotlin Playgroundのリンクを以下に貼ります。
https://pl.kotl.in/Q0pi-t1Am?theme=darcula
感想
nullableな関数型の実行には ?.invoke()
を使用します。
invoke()を調べることによってすこしKotlinの理解を深めた気がします。
ブラウザで手軽に試せるPlayground便利です。
関連