Kotlin暂停娱乐 [英] Kotlin suspend fun

查看:92
本文介绍了Kotlin暂停娱乐的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有Kotlin界面

interface FileSystem {
    suspend fun getName(path: Path): List<String>
}

如何从Java调用它?什么是

How I can call it from Java? What is

Continuation <? super List<String>>

推荐答案

Kotlin使用常规的基于堆栈的调用约定和 continuation-passing样式(CPS)的组合来实现协程.为此,它通过添加隐式参数在所有suspend fun上执行 CPS转换,您可以使用该对象从调用函数的位置继续执行程序.这就是Kotlin如何设法在函数体内暂停执行的诀窍:提取延续对象,将其保存在某个地方,然后使函数返回(尚未产生其值).稍后,它可以通过调用延续对象来实现跳入功能主体中间的效果.

Kotlin implements coroutines using a mix of the regular stack-based calling convention and the continuation-passing style (CPS). To achieve that, it performs a CPS transformation on all suspend funs by adding an implicit parameter, an object you can use to continue the program from the place where the function was called. That's how Kotlin manages to pull off the trick to suspend the execution within your function's body: it extracts the continuation object, saves it somewhere, and then makes your function return (without yet having produced its value). Later on it can achieve the effect of jumping into the middle of your function body by invoking the continuation object.

延续基本上是一个回调对象,就像异步Java API中熟悉的那样.可悬挂函数不返回其结果,而是将其结果传递给延续.要从Java调用suspend fun,您必须创建这样的回调.这是一个示例:

The continuation is basically a callback object, just like those familiar from async Java APIs. Instead of returning its result, the suspendable function passes its result to the continuation. To call a suspend fun from java, you'll have to create such a callback. Here's an example:

Continuation<List<String>> myCont = new Continuation<List<String>>() {
    @Override public void resume(List<String> result) {
        System.out.println("Result of getName is " + result);
    }
    @Override public void resumeWithException(Throwable throwable) {
        throwable.printStackTrace();
    }
    @NotNull @Override public CoroutineContext getContext() {
        return Unconfined.INSTANCE;
    }
};

注意:以上仅适用于实验性协程.在最终的API中,只有一种恢复方法:resumeWith(result: Result<T>),其中Result是结果类型和internal class Failure的区分后的并集,这使得从Java无法访问它.

NOTE: The above works only with experimental coroutines. In the final API there's just one resumption method: resumeWith(result: Result<T>) where Result is a discriminated union of the result type and the internal class Failure, which makes it inaccessible from Java.

让我们还创建FileSystem接口的模拟实现:

Let's also create a mock implementation of the FileSystem interface:

class MockFs : FileSystem {
    override suspend fun getName(path: Path): List<String> {
        suspendCoroutine<Unit> {
            println("getName suspended")
        }
        println("getName resumed")
        return listOf("usr", "opt")
    }
}

现在我们准备从Java调用它:

Now we're ready to call it from Java:

Object result = new MockFs().getName(Paths.get(""), myCont);
System.out.println("getName returned " + result);

它打印

getName suspended
getName returned
kotlin.coroutines.experimental.intrinsics.CoroutineSuspendedMarker@6ce253f1

getName()返回了一个特殊的标记对象,该对象指示函数已被挂起.一旦恢复,该函数会将其实际结果传递给我们的回调.

getName() returned a special marker object that signals the function got suspended. The function will pass its actual result to our callback, once it resumes.

现在让我们改进MockFs,以便我们可以访问延续:

Let us now improve MockFs so we can get access to the continuation:

class MockFs : FileSystem {
    var continuation : Continuation<Unit>? = null

    override suspend fun getName(path: Path): List<String> {
        suspendCoroutine<Unit> {
            continuation = it
            println("getName suspended")
        }
        println("getName resumed")
        return listOf("usr", "opt")
    }
}

现在,我们将能够手动恢复继续.我们可以使用以下代码:

Now we'll be able to manually resume the continuation. We can use this code:

MockFs mockFs = new MockFs();
mockFs.getName(Paths.get(""), myCont);
mockFs.getContinuation().resume(Unit.INSTANCE);

这将打印

getName suspended
getName resumed
Result of getName is [usr, opt]

在现实生活中,当结果可用时,可挂起的函数将使用某种机制来使自身恢复.例如,如果它是一些异步API调用的包装,它将注册一个回调.当异步API调用回调时,它将依次调用我们的延续.您不需要像我们的模拟代码中那样手动恢复它.

In real life a suspendable function will use some mechanism to get itself resumed when the result becomes available. For example, if it's a wrapper around some async API call, it will register a callback. When the async API invokes the callback, it will in turn invoke our continuation. You shouldn't need to manually resume it like we did in our mock code.

A suspend fun还可以选择直接返回其结果.例如,使用此MockFs代码

A suspend fun also has the option to just return its result directly. For example, with this MockFs code

class MockFs : FileSystem {
    override suspend fun getName(path: Path) = listOf("usr", "opt") 
}

在Java中我们只能说

in Java we can just say

System.out.println(new MockFs().getName(Paths.get(""), myCont));

,它将打印[usr, opt].我们甚至可以传入Continuation的空实现.

and it will print [usr, opt]. We could even have passed in an empty implementation of Continuation.

最苛刻的情况发生在您事先不知道函数是否将自身挂起的情况下.在这种情况下,一种好的方法是在呼叫站点上编写以下内容:

The most demanding case happens when you don't know in advance whether the function will suspend itself or not. In such a case a good approach is to write the following at the call site:

Object retVal = mockFs.getName(Paths.get(""), myCont);
if (retVal != IntrinsicsKt.getCOROUTINE_SUSPENDED()) {
    myCont.resume((List<String>) retVal);
}

否则,您将必须复制处理函数结果的代码.

Otherwise you'll have to duplicate the code that handles the function's result.

这篇关于Kotlin暂停娱乐的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆