Kotlin - 内联参数回调的非法使用

Kotlin - Illegal usage of inline parameter callback

我正在将 functionlambda 转换为 parameterinline function 以提高性能。

我有 MutableList<(Authenticate) -> Unit> 类型 lambdalist 变量作为 class 中的数据成员。当我尝试将 lambda parameter 添加到 list.

Kotlin 编译器说:

Illegal usage of inline parameter callback

这是代码

// 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,这消除了拥有函数对象的开销。

例如,这大致导致 main 这里:

fun withLambda(lambda: () -> Unit) {
    lambda()
}

inline fun inlinedLambda(lambda: () -> Unit) {
    lambda()
}

fun main(args: Array<String>) {
    withLambda { println("Hello, world") }
    inlinedLambda { println("Hello, world") }
}

正在转换为:

fun main(args: Array<String>) {
    withLambda { println("Hello, world") }
    println("Hello, world") // <- Directly inserted!
}

如果你有

pendingAuthenticateCallbacks += callback

这是不可能的,因为 callback 必须是一个对象才能添加到列表中。

您需要添加 noinline 修饰符。

粗略地说,不能将内联 lambda 视为对象,因为它并不真正存在 作为对象。它直接使用而不是作为对象创建。

当然,您可以创建一个包含 lambda 的函数:

pendingAuthenticateCallbacks += { callback() } // Not a good idea

但这将完全破坏内联的意义(不要这样做!)。

但是,使参数 noinline 意味着您的方法现在具有零个可内联的 lambda 参数,因此您最好删除 inline 修饰符,因为性能优势将很小。

编译器应该识别这个:

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.


使用 lambdas 和 reified generic type parameters 时,内联方法的主要原因是性能 。从 Kotlin 1.1 开始,也可以为没有支持字段的属性提供内联 属性 访问器。


简而言之,如果你没有lambda参数(或者没有reified类型参数,在这种情况下你必须),标记一个函数通常是没有意义的作为 inline.