Kotlin暂停娱乐

时间:2018-08-12 12:50:01

标签: kotlin kotlin-coroutines

我有Kotlin界面

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

如何从Java调用它?什么是

Continuation <? super List<String>>

enter image description here

1 个答案:

答案 0 :(得分:3)

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

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

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访问。

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

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

现在我们准备从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()返回了一个特殊的标记对象,该对象指示函数已被挂起。一旦恢复,该函数会将其实际结果传递给我们的回调。

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

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")
    }
}

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

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调用回调时,它将依次调用我们的延续。您不需要像我们的模拟代码中那样手动恢复它。

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

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

在Java中我们只能说

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

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

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

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

否则,您将不得不复制处理函数结果的代码。