如何覆盖 java class 中的挂起函数

How to Override a suspend function in java class

考虑以下 kotlin 接口:

LoginRepository.kt

interface LoginRepository {
    suspend fun login(): LoginResponse
}

LoginRepo.java

class LoginRepo implements LoginRepository {
    public Object login(@NonNull Continuation<? super LoginResponse> $completion) {
        api.login((result) -> {
            ContinuationUtilsKt.resumeContinuationWithSuccess($completion, result);
        });

        return null;
    }
}

ContinuationUtils.kt

fun <T> resumeContinuationWithSuccess(cont: Continuation<T>, value: T) {
    cont.resumeWith(Result.success(value))
}

我试图将代码深入到它的基本部分,即在 java class 中覆盖的挂起函数进行 API 调用和 return 是使用延续对象的成功或失败延续,return 是 null。

但是,方法 LoginRepository.login 调用时 returns null.

LoginRepo 中重写的方法签名由 IDE 生成。

我应该 return 一些对象而不是 null 吗?或者我缺少的其他内容。

我真的不认为你应该这样做。在 Kotlin 中用于实现它的功能和 类 是 internal/private 并且从 Java 侧隐藏。

基本上,您需要拦截原始的 Continuation 并使用您的 return 值恢复新的 returned Continuation。然后 return Intrinsics.COROUTINE_SUSPENDED 表示您没有同步 returning 一个值。如果 return 值是 Intrinsics.COROUTINE_SUSPENDED 以外的任何值,那么我认为它假设您直接 returning 挂起函数声明的 return 值。

虽然此代码可能有效,但它可能无法处理所有边缘情况,并且可能不会在发生崩溃时提供有用的堆栈跟踪。标准库实现要复杂得多。

class LoginRepo implements LoginRepository {
    public Object login(@NonNull Continuation<? super LoginResponse> $completion) {
        Continuation<? super LoginResponse> cont = IntrinsicsKt.intercepted($completion);

        api.login((result) -> {
            ContinuationUtilsKt.resumeContinuationWithSuccess(cont, result);
        });

        return IntrinsicsKt.getCOROUTINE_SUSPENDED();
    }
}

Kotlin 与 Java 的互操作性并不真正包含挂起函数。暂停函数是 Kotlin 特有的,它们很难从 Java.

调用和实现

在大多数情况下,我建议甚至不要尝试处理来自 Java 的延续和可暂停代码,而是在 Kotlin 中创建小型“适配器”。这些适配器会将可挂起的代码从 Java 翻译成更有用的代码。例如,在 Kotlin 中,可以很容易地在挂起函数和 CompletableFuture 之间进行双向转换。

你的情况比较棘手,因为你需要实现一个接口。尽管如此,Kotlin 还是有办法处理这个问题的。例如,我们可以在 Kotlin 中创建 LoginRepository 的抽象实现。它将提供 login(),但您将在 Java 中实现所有剩余的方法。我们可以使用组合而不是继承来做类似的事情,方法是在 Kotlin 中创建 LoginRepository 的 non-abstract 实现(从所有不相关的函数中抛出错误)并从 Java [=33] 委托给它=].或者我们可以创建一个静态函数,执行从 callback-based API 到挂起 API 的转换。这个解决方案是最灵活的,但是我们需要弄乱来自 Java:

的一些协程内部结构
@file:JvmName("SuspendUtils")

// utility function in Kotlin, it converts callback API to a suspend function
suspend fun login(api: Api): LoginResponse = suspendCoroutine { cont ->
    api.login { cont.resume(it) }
}
public static class LoginRepo implements LoginRepository {
    private Api api = new Api();

    @Nullable
    @Override
    public Object login(@NotNull Continuation<? super String> $completion) {
        return SuspendUtils.login(api, $completion);
    }
}