代码之家  ›  专栏  ›  技术社区  ›  Kasim Rangwala

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

  •  1
  • Kasim Rangwala  · 技术社区  · 7 年前

    我正在转换我的 function lambda parameter 进入 inline function 用于性能改进。

    我有 list 属于 类型 MutableList<(Authenticate) -> Unit> 变量作为类中的数据成员。当我尝试添加时 lambda parameter 进入 列表 .

    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()
                                    )))
                                }
                    }
                })
            }
        }
    }
    
    1 回复  |  直到 7 年前
        1
  •  12
  •   Salem    7 年前

    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 作为性能优势的修改器将是最小的。

    编译器应该认识到这一点:

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


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


    简而言之,如果没有lambda参数(或 具体化 键入参数,在这种情况下 必须 ),将函数标记为 内联 .