I'm converting my function
having lambda
as parameter
into inline function
for performance improvement.
I have list
of lambda
of type MutableList<(Authenticate) -> Unit>
variable as data member in class. When I try to adding lambda parameter
into the list
.
Kotlin compiler says:
Illegal usage of inline parameter callback
Here is the code
// Some code skipped
object Odoo {
val pendingAuthenticateCallbacks = mutableListOf<(Authenticate) -> Unit>()
inline fun authenticate(
login: String, password: String, database: String,
quick: Boolean = false, crossinline callback: Authenticate.() -> Unit
) {
// Following statement has error saying
// Illegal usage of inline parameter callback. add 'noinline' modifier to parameter declaration.
pendingAuthenticateCallbacks += callback
// Error in above statement
if (pendingAuthenticateCallbacks.size == 1) {
// Retrofit2 Object boxing code skipped
val call = request.authenticate(requestBody)
call.enqueue(object : Callback<Authenticate> {
override fun onFailure(call: Call<Authenticate>, t: Throwable) {
(pendingAuthenticateCallbacks.size - 1 downTo 0)
.map { pendingAuthenticateCallbacks.removeAt(it) }
.forEach {
it(Authenticate(httpError = HttpError(
Int.MAX_VALUE,
t.message!!
)))
}
}
override fun onResponse(call: Call<Authenticate>, response: Response<Authenticate>) {
(pendingAuthenticateCallbacks.size - 1 downTo 0)
.map { pendingAuthenticateCallbacks.removeAt(it) }
.forEach {
it(Authenticate(httpError = HttpError(
response.code(),
response.errorBody()!!.string()
)))
}
}
})
}
}
}
Inlining inserts the code in the lambda directly into the call site, which removes the overhead of having a function object.
For example, this roughly results in main
here:
fun withLambda(lambda: () -> Unit) {
lambda()
}
inline fun inlinedLambda(lambda: () -> Unit) {
lambda()
}
fun main(args: Array<String>) {
withLambda { println("Hello, world") }
inlinedLambda { println("Hello, world") }
}
being converted to this:
fun main(args: Array<String>) {
withLambda { println("Hello, world") }
println("Hello, world") // <- Directly inserted!
}
If you have
pendingAuthenticateCallbacks += callback
This is impossible because callback
must be an object in order for it to be added to the list.
You need to add the noinline
modifier.
A rough approximation would be to say that an inlined lambda cannot be treated as an object, as it doesn't really exist as an object. It is used directly instead of being created as an object.
Of course, you could create a containing lambda:
pendingAuthenticateCallbacks += { callback() } // Not a good idea
but this would entirely defeat the point of inlining (don't do this!).
However, making the parameter noinline
would mean your method now has zero lambda parameters that can be inlined, so you might as well just remove the inline
modifier as performance benefit would be minimal.
The compiler should recognize this:
Note that if an inline function has no inlinable function parameters and no reified type parameters, the compiler will issue a warning, since inlining such functions is very unlikely to be beneficial.
The main reason for inlining methods is for performance when using lambdas and for reified
generic type parameters. As of Kotlin 1.1, it is also possible to have an inline property accessor for properties without a backing field.
In short, if you have no lambda parameters (or no reified
type parameters, in which case you must), it is usually pointless to mark a function as inline
.