Kotlinのnoinlineとcrossinlineをいつ使うのか

Kenji Abe
5 min readApr 5, 2020

--

Photo by Sven Brandsma on Unsplash

Kotlinの noinlinecrossinline がどういうときに必要になるかを調べました。

noinline

noinlineinline 関数に2つ関数を渡してる場合などで、片方が inline として扱えない関数につけるものになります。

inline fun f(block1: () -> Unit, noinline block2: () -> Unit) {

}

使い方は分かるのですが、まず、そもそも inline 関数として扱えないものにどういうのがあるかについて調べました。

inline関数として扱えないものは、受け取った関数を inline 関数ではない関数に渡すときです。

// inline関数
inline fun f(block: () -> Unit) {
f2(block) // compile error
}
// not inline関数
fun f2(block: () -> Unit) {
block()
}

上の例では inline 関数(f)から inline ではない関数(f2)に引数の関数を渡しています。このときコンパイルエラーになるため inline 関数にすることができません。

もう1つのパターンとして、インスタンスメソッドも同様です。

class Sample {
fun run(block: () -> Unit) {
block()
}
}
inline fun f(block: () -> Unit) {
val sample = Sample()
sample.run(block) // compile error
}

これを解決するには、2つ方法があります。

まずは単純に inline を外してしまう方法です。

// not inline関数
fun f(block: () -> Unit) {
f2(block) // ok
}
// not inline関数
fun f2(block: () -> Unit) {
block()
}

もう一つが呼び出し先も inline 関数にする

// inline関数
inline fun f(block: () -> Unit) {
f2(block) // ok
}
// inline関数
inline fun f2(block: () -> Unit) {
block()
}

上記を踏まえて、 noinline が必要になるパターンとしては以下のような感じです。

// inline関数
inline fun f(block1: () -> Unit, noinline block2: () -> Unit) {
block1()
f2(block2)
}
// not inline関数
fun f2(block: () -> Unit) {
block()
}

上の例で block1 は inline で処理できるが、block2 のほうは inline ではない関数を呼んでるので noinline をつけて対応する感じになります。

crossinline

こちらも inline 関数を使用する際に必要になる場合があります。使いかたは引数の関数に crossinline をつけるだけです。

inline fun f(crossinline block: () -> Unit) {

}

crossinline についても必要な状況がどういうときかを調べました。

こちらも inline 関数から inline ではない関数を呼び出すときに必要になる場合があります。

// inline関数
inline fun f(block: () -> Unit) {
f2 {
block() // compile error
}
}
// not inline関数
fun f2(block: () -> Unit) {
block()
}

先程、 inline のほうとは違って、関数を渡すのではなく、lambda式から受け取った関数を実行しています。

また、以下のような状況も同様です。

// inline関数
inline fun f(block: () -> Unit) {
Executors.newSingleThreadExecutor().execute {
block() // compile error
}
}

これを crossinline を使うことで解決することができます。

// inline関数
inline fun f(crossinline block: () -> Unit) {
f2 {
block() // ok
}
}
// not inline関数
fun f2(block: () -> Unit) {
block()
}

別の方法としては、 呼び出し元も呼び出しも両方とも inline にするか、逆に inline を外すかということもできます。

参考

--

--

Kenji Abe
Kenji Abe

Written by Kenji Abe

Programmer / Gamer / Google Developers Expert for Android, Kotlin / @STAR_ZERO

No responses yet