【问题标题】:Kotlin extension lambdas vs regular lambdasKotlin 扩展 lambdas 与常规 lambdas
【发布时间】:2023-04-02 16:49:01
【问题描述】:

根据以下源代码,常规 lambda 似乎可以与扩展 lambda 互换。

fun main(args: Array<String>) {

    val numbers = listOf(1, 2, 3)

    filter(numbers, predicate)
    filter(numbers, otherPredicate)

    println("PREDICATE: ${predicate} " +
        "\nOTHERPREDICATE: ${otherPredicate} " +
        "\nEQUALITY: ${predicate==otherPredicate}")
}

val predicate : Int.() -> Boolean = {this % 2 != 0}
val otherPredicate : (Int) -> Boolean = {it % 2 != 0}


fun filter(list: List<Int>, predicate:(Int) -> Boolean) {
    for(number in list){
        if(predicate(number)){
            println(number)
        }
    }
}

输出(我关心)如下:

PREDICATE: kotlin.Int.() -> kotlin.Boolean 
OTHERPREDICATE: (kotlin.Int) -> kotlin.Boolean 
EQUALITY: false

问题是为什么这些 lambda 可以互换?不应该有所不同吗?编译器是否在做一些“聪明”的事情?

【问题讨论】:

    标签: lambda kotlin extension-methods


    【解决方案1】:

    区别

    它不能完全互换,因为“扩展 lambdas”,技术上称为 lambdas with receiver,可以在接收器上调用,这对于常规 lambdas 是不可能的:

    predicateWithReceiver(2) //OK
    2.predicateWithReceiver() //OK
    
    regularPredicate(2) //OK
    2.regularPredicate //Not OK
    

    带有接收器的 Lambda 可以作为带有参数的普通函数调用,但也可以直接在其接收器对象上调用(类似于扩展)。更重要的部分是此类 lambda 在调用者站点上的外观,即您不需要使用限定符来访问此类 lambda 中该接收者的可见成员。

    编译

    这是通过编译器技术实现的。下面演示了2.regularPredicate 在字节码级别的外观(显示为反编译的 Java):

      Function1 predicateWithReceiver = ...;
      predicateWithReceiver.invoke(2);
    

    它看起来像一个常规的函数调用,翻译由编译器负责。

    编辑

    对于像filter 这样的高阶函数,它并没有真正的区别。看看它是如何编译的(再次描述为 Java):

    public static final void filter(@NotNull List list, @NotNull Function1 predicate) {
    
      //...
         if ((Boolean)predicate.invoke(number)) {
            System.out.println(number);
         }
      }
    
    }
    

    过滤器函数采用Function1 的实例。带有接收器的常规和 lambda 都被编译为完全这样的对象。因此,如何在 Kotlin 代码中定义参数 predicate 并没有什么区别。

    【讨论】:

    • 即使我把过滤器函数改成这样:fun filter(list: List, predicate:Int.() -> Boolean):Unit{ for(number in list){ if( number.predicate()){ println(number) } } } 它还在工作。
    • @LiTTle 请参阅编辑部分。
    • 带有接收器的 lambda 的美妙之处在于,在 lambda 的主体中你会得到一个隐含的this,指向接收器对象。因此,您可以使用它直接访问接收器对象及其成员,而无需任何额外的限定符,例如 it
    猜你喜欢
    • 1970-01-01
    • 2016-05-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-04-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多