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

时间:2017-11-21 18:05:58

标签: android lambda kotlin inline

我正在将function lambda parameter inline function转换为list以提升效果。

我有lambda MutableList<(Authenticate) -> Unit>类型lambda parameter变量作为类中的数据成员。当我尝试将list添加到// 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() ))) } } }) } } }

Kotlin编译器说:

  

非法使用内联参数回调

这是代码

<ul id="cart-sidebar" class="mini-products-list" ng-init="cartArray.total = {}">
    <li class="item odd" ng-if="cartArray.length === 0">No items in your cart..</li>
    <li class="item odd" ng-repeat='y in cartArray'>
        <a href="shopping_cart.html" class="product-image"><img ng-src="" alt="" width="65"></a>
        <div class="product-details">
            <a href="#" title="Remove This Item" class="remove-cart" ng-click="removeFromCart(y)"><i class="pe-7s-trash"></i></a>
            <p class="product-name"><a href="shopping_cart.html">{{y.prodName}}</a> </p>
            <strong>1</strong> x <span class="price" ng-init='cartArray.total.prodAmt = cartArray.total.prodAmt + y.prodAmt'>Rs. {{y.prodAmt}}</span>
        </div>
    </li>
</ul>
<div ng-if="cartArray.length > 0" class="top-subtotal">Subtotal: <span class="price">{{cartArray.total.prodAmt }}</span></div>

1 个答案:

答案 0 :(得分:8)

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

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

  

请注意,如果内联函数没有inlinable函数参数且没有reified类型参数,编译器将发出警告,因为内联这些函数不太可能有用。

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

简而言之,如果您没有lambda参数(或没有reified类型参数,在这种情况下必须),将函数标记为{{1}通常没有意义}。