Kotlin 暂停乐趣
Kotlin suspend fun
我有 Kotlin 接口
interface FileSystem {
suspend fun getName(path: Path): List<String>
}
如何从 Java 调用它?什么是
Continuation <? super List<String>>
Kotlin 结合使用常规的基于堆栈的调用约定和 continuation-passing 样式 (CPS) 来实现协程。为此,它通过添加隐式参数对所有 suspend fun
执行 CPS 转换,该对象可用于从调用函数的位置继续执行程序.这就是 Kotlin 如何设法实现在函数体内暂停执行的技巧:它提取延续对象,将其保存在某个地方,然后使您的函数 return (尚未产生其值)。后面可以通过调用continuation对象达到跳到你函数体中间的效果。
continuation 基本上是一个回调对象,就像我们熟悉的异步 Java APIs 一样。可挂起函数不是 returning 其结果,而是将其结果传递给延续。要从 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;
}
};
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
接口的模拟实现:
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()
returned 一个特殊的标记对象,表示函数已暂停。一旦恢复,该函数会将其实际结果传递给我们的回调。
现在让我们改进 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 调用的包装器,它将注册一个回调。当 async API 调用回调时,它会依次调用我们的延续。您不需要像我们在模拟代码中那样手动恢复它。
A suspend fun
也可以选择直接 return 其结果。例如,使用此 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);
}
否则您将不得不复制处理函数结果的代码。
我有 Kotlin 接口
interface FileSystem {
suspend fun getName(path: Path): List<String>
}
如何从 Java 调用它?什么是
Continuation <? super List<String>>
Kotlin 结合使用常规的基于堆栈的调用约定和 continuation-passing 样式 (CPS) 来实现协程。为此,它通过添加隐式参数对所有 suspend fun
执行 CPS 转换,该对象可用于从调用函数的位置继续执行程序.这就是 Kotlin 如何设法实现在函数体内暂停执行的技巧:它提取延续对象,将其保存在某个地方,然后使您的函数 return (尚未产生其值)。后面可以通过调用continuation对象达到跳到你函数体中间的效果。
continuation 基本上是一个回调对象,就像我们熟悉的异步 Java APIs 一样。可挂起函数不是 returning 其结果,而是将其结果传递给延续。要从 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;
}
};
NOTE: The above works only with experimental coroutines. In the final API there's just one resumption method:
resumeWith(result: Result<T>)
whereResult
is a discriminated union of the result type and theinternal class Failure
, which makes it inaccessible from 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()
returned 一个特殊的标记对象,表示函数已暂停。一旦恢复,该函数会将其实际结果传递给我们的回调。
现在让我们改进 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 调用的包装器,它将注册一个回调。当 async API 调用回调时,它会依次调用我们的延续。您不需要像我们在模拟代码中那样手动恢复它。
A suspend fun
也可以选择直接 return 其结果。例如,使用此 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);
}
否则您将不得不复制处理函数结果的代码。