3
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

【小ネタ】Kotlinでnullableな関数型を定義するときの '?.invoke()' について

Last updated at Posted at 2019-10-06

高階関数やラムダ式で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便利です。

関連

3
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?