【问题标题】:Kotlin - Illegal usage of inline parameter callbackKotlin - 非法使用内联参数回调
【发布时间】:2017-11-21 18:05:57
【问题描述】:

我正在将我的function 转换为lambdaparameterinline function 以提高性能。

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

Kotlin 编译器说:

内联参数回调的非法使用

这里是代码

// 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()
                                )))
                            }
                }
            })
        }
    }
}

【问题讨论】:

    标签: android lambda kotlin inline


    【解决方案1】:

    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 修饰符,因为性能优势将是最小的。

    编译器应该可以识别:

    请注意,如果内联函数没有可内联的函数参数,也没有具体的类型参数,编译器会发出警告,因为内联此类函数不太可能有益。


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


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

    【讨论】:

    • 感谢您的精彩解释。所以,我需要从function 中删除inline 关键字并将noinline 关键字添加到callback 参数。对吗?
    • @Ka7Im1011 noinline如果函数不是inline会导致编译错误,所以你只需要从函数中删除inline关键字。
    • 抱歉您的答案已更新。所以,我可以使用带有内联函数的pendingAuthenticateCallbacks += { callback() } 获得更好的性能。对吗?
    • @Ka7Im1011 我的意思是添加一个可能的例子。我应该澄清这一点(我会编辑它)但不,最好只制作函数 not inline.
    • 再次感谢您的详细解释。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-05-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-01-17
    相关资源
    最近更新 更多