【问题标题】:In Kotlin, how do I convert "CompletableFuture<Optional<T>>" to "Flow<T?>"?在 Kotlin 中,如何将“CompletableFuture<Optional<T>>”转换为“Flow<T?>”?
【发布时间】:2021-10-02 19:41:50
【问题描述】:

我正在尝试将 CompletableFuture&lt;Optional&lt;T&gt;&gt; 转换为 Flow&lt;T?&gt;。我正在尝试编写的扩展函数是

fun <T> CompletableFuture<Optional<T>>.asFlowOfNullable(): Flow<T?> =
    this.toMono().map { (if (it.isPresent) it.get() else null) }.asFlow()

但它失败了,因为 asFlow() 不存在可空类型,AFAICT 基于其定义。

那么,如何将CompletableFuture&lt;Optional&lt;T&gt;&gt; 转换为Flow&lt;T?&gt;

编辑 1:

这是我到目前为止的想法。感谢您的反馈。

import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flowOf
import java.util.Optional
import java.util.concurrent.CompletableFuture

fun <T> Optional<T>.orNull(): T? = orElse(null)

fun <T> CompletableFuture<Optional<T>>.asFlowOfNullable(): Flow<T?> = flowOf(this.join().orNull())

仅供参考,就我而言,它使用 Axon 的 Kotlin 扩展 queryOptional,我现在可以这样写:

inline fun <reified R, reified Q> findById(q: Q, qgw: QueryGateway): Flow<R?> {
    return qgw.queryOptional<R, Q>(q).asFlowOfNullable()
}

我将推迟一段时间使用上述模式创建评论作为允许反馈的答案。

编辑 2: 由于下面指出编辑 1 中的 asFlowOfNullable 会阻塞线程,所以我现在从 @Joffrey 开始:

fun <T> Optional<T>.orNull(): T? = orElse(null)

fun <T> CompletableFuture<Optional<T>>.asDeferredOfNullable(): Deferred<T?> = thenApply { it.orNull() }.asDeferred()

编辑 3:感谢 @Tenfour04 和 @Joffrey 提供的有用意见。 :)

【问题讨论】:

  • 在 Kotlin 中,我们通常不使用 Flow 来表示单个项目。使用返回值的简单挂起函数更自然。为什么你需要一个流程?
  • 因为我使用 Axon 的 Kotlin 扩展来调用 QueryGateway.query&lt;R,Q&gt;(query:Q): CompletableFuture&lt;R&gt;,其中只有一个项目或 null。
  • 这解释了为什么你有一个 CompletableFuture,而不是为什么你想将它转换为 Flow 而不是挂起函数或 Deferred。
  • 由于 join 调用,您在编辑 1 中的代码将阻塞调用线程直到完成。

标签: kotlin reactive-programming kotlin-extension kotlin-coroutines


【解决方案1】:

要使用以下扩展,您需要 jdk8 协程库:

implementation "org.jetbrains.kotlinx:kotlinx-coroutines-jdk8:$1.5.0"

我不确定您正在使用的 asFlow() 函数来自哪里,但我认为没有它也可以工作。拥有单个项目的 Flow 对我来说似乎有点奇怪,因为它可能只是一个 suspend 函数,或者如果您需要它作为传递的对象,一个 Deferred,它旨在返回单个结果和因此,它更类似于 Future 而不是 Flow。

fun <T> CompletableFuture<Optional<T>>.asFlowOfNullable(): Flow<T?> =
    flow { emit(await().orElse(null)) }

作为挂起函数:

suspend fun <T> CompletableFuture<Optional<T>>.awaitNullable(): T? = 
    await().orElse(null))

作为延期:

fun <T> CompletableFuture<Optional<T>>.asDeferredNullable(): Deferred<T?> =
    thenApply { it.orElse(null) }.asDeferred()

【讨论】:

  • 查看我上面的回复以获得动力。我尝试了你的两个示例,并得到以下编译错误:Kotlin: Unresolved reference: await。我错过了一些进口吗?
  • 流程很尴尬。我能想到的唯一情况是你将它传递给一些只接受 Flow 的 API。即使是 Deferred 也有一些有限的情况,它比简单地暴露一个挂起函数更有意义。仅当您需要协调多个异步操作时才需要 Deferred。
  • @MatthewAdams Deferred 本质上是一个延迟值,而Flow 并不能说明它是否会产生 0、1 或更多值。我会认真避免将 Flow 用于单个值,除非您想在一个地方使用此流,并且您还希望接受或返回具有可能更多值的其他流。
  • @MatthewAdams 我不是这个答案的作者,但暂停版本已经由@Tenfour04 提供。在此答案中称为awaitNullable。它不返回Deferred,它只返回T?,因为“异步”已经由suspend 修饰符表示。另一方面,如果您将CompletableFuture 转换为Deferred,则无需暂停,因为您只是立即将未来类型转换为另一种类型 - 无需等待。
  • @MatthewAdams 关于提交的问题,API 公开返回普通值(或单位)的 suspend 函数比公开返回类型为 Deferred 的函数更合适。
猜你喜欢
  • 2021-11-02
  • 1970-01-01
  • 1970-01-01
  • 2021-12-29
  • 2016-02-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-08-18
相关资源
最近更新 更多