KotlinFlow,如何使用override collect函数

7d7tgy0s  于 2023-08-06  发布在  Kotlin
关注(0)|答案(2)|浏览(122)

学习Flow的时候,看到一个返回Flow的代码.但不明白传入的“块”是如何通过调用

flow.collect { value ->
    println(value)
}

字符串
相关代码;

internal inline fun <T> unsafeFlow(crossinline block: suspend FlowCollector<T>.() -> Unit): Flow<T> {
    return object : Flow<T> {
        override suspend fun collect(collector: FlowCollector<T>) {
            collector.block()
        }
    }
}

suspend fun FlowCollector<Int>.loopFunc() {
    for (i in 1..5) {
        emit(i)
    }
}

val flow: Flow<Int> = unsafeFlow<Int> {
    loopFunc()
}

flow.collect { value ->
    println(value)
}

nwnhqdif

nwnhqdif1#

免责声明:在评论中讨论过这个问题后,很明显混淆主要来自于尾随的lambdas和SAM conversions。作者还问了这个代码实际上是如何工作的。
让我们从基础开始:

  • FlowCollector-流中项目的消费者。它是一个被动的一面,项目被发射(推)到它。
  • Flow-项目来源。可以通过向其提供收集器来使用它们。然后流发出到我们的收集器。
  • loopFunc()-我们可以称之为生成器函数。它接收一个收集器,并通过将项发射到该收集器中来生成项。它在某种程度上类似于流,因为它接受收集器并向其发出。但它并不是表示项目来源的标准方式-通常,我们使用流。
  • unsafeFlow()-它将生成器函数转换为流。它创建了一个流,如果我们从它收集(向它提供一个收集器),它所做的唯一事情就是委托给一个存储的生成器函数。

现在解释一下这段代码:

flow.collect { value ->
    println(value)
}

字符串
它使用尾随lambda语法和SAM转换。它与以下内容相同:

flow.collect(object : FlowCollector<Int> {
    override suspend fun emit(value: Int) {
        println(value)
    }
})


总结一下,代码流程是这样的:
1.我们使用unsafeFlow()创建一个流。此流存储loopFunc()生成器函数,并在收集时简单地委托给它。
1.我们创建一个收集器,它只是打印发射到它的项目。
1.我们通过向流提供收集器来从流中收集数据。
1.在unsafeFlow()中创建的流只是将收集器传递给loopFunc()

  1. loopFunc()运行一个循环,用后续的项调用我们的收集器。
    1.当循环结束时,我们从loopFunc()返回,然后从flow.collect()函数返回,因此代码可以继续。
    整个代码可以简化为:
FlowCollector<Int> { value -> println(value) }.loopFunc()


但是它在中间使用了一个Flow,这增加了复杂性。

p8h8hvxi

p8h8hvxi2#

好,你看这个理解对不对。
在kotlinx.coroutines.flow中,它有一个扩展函数,它接受一个函数的参数。

@Deprecated(level = DeprecationLevel.HIDDEN, message = "Backwards compatibility with JS and K/N")
public suspend inline fun <T> Flow<T>.collect(crossinline action: suspend (value: T) -> Unit): Unit =
    collect(object : FlowCollector<T> {
        override suspend fun emit(value: T) = action(value)
    })

字符串
所以

flow.collect { value ->
    println(value)
}


用lambda作为参数调用它。在扩展函数中,它调用Flow示例上的collect函数(从unsafeFlow()返回),其中FlowCollector对象具有override override suspend fun emit(value: T) = action(value)

collect(object : FlowCollector<T> {
        override suspend fun emit(value: T) = action(value)
    })


在unsafeFlow()中:

internal inline fun <T> unsafeFlow(crossinline block: suspend FlowCollector<T>.() -> Unit): Flow<T> {
    return object : Flow<T> {
        override suspend fun collect(collector: FlowCollector<T>) {
            collector.block()
        }
    }
}


它传入了一个crossinline block: suspend FlowCollector<T>.() -> UnitunsafeFlow函数签名中的lambda表达式block可以像FlowCollector的成员函数一样调用。这是通过Kotlin中带有接收器的函数字面量的概念实现的(Kotlin中带有接收器的函数字面量允许您定义被调用的函数,就好像它们是特定接收器类型的成员函数一样)。
所以呢
因此,当调用从unsafeFlow()返回的Flow的collect(collector: FlowCollector<T>)时,在其中调用block(),其调用覆盖emit(value:T),即println(value)
希望这就是代码片段发生的情况。

更新:@broot是正确的,谢谢!。fun interface FlowCollector<in T>是功能(SAM)接口,因此

flow.collect { value ->
    println(value)
}


它将lambda表达式转换为接口实现,它与:

flow.collect(object : FlowCollector<Int> {
    override suspend fun emit(value: Int) {
        println(value)
    }
})

相关问题