如何运行通过反射挂起方法?
How to run suspend method via reflection?
有一个协程块可以 运行s 挂起函数。
但是我通过invoke
通过反射调用函数。这是java风格的调用,显然简单的调用是行不通的。
有没有办法 运行 异步反映方法?如何等待这个方法?
import kotlin.coroutines.experimental.*
class TestClass(val InString: String) {
suspend fun printString() {
println(InString)
}
}
fun launch(context: CoroutineContext, block: suspend () -> Unit) =
block.startCoroutine(StandaloneCoroutine(context))
private class StandaloneCoroutine(override val context: CoroutineContext): Continuation<Unit> {
override fun resume(value: Unit) {}
override fun resumeWithException(exception: Throwable) {
val currentThread = Thread.currentThread()
currentThread.uncaughtExceptionHandler.uncaughtException(currentThread, exception)
}
}
fun main(args: Array<String>) {
launch(EmptyCoroutineContext) {
val a = TestClass("TestString");
for (method in a.javaClass.methods) {
if (method.name == "printString")
method.invoke(a) // Exception in thread "main" java.lang.IllegalArgumentException: wrong number of arguments
}
}
}
更新
由于 Kotlin 1.3 反射原生支持通过 KFunction.callSuspend
和 KFunction.callSuspendBy
调用挂起函数,因此不再需要上述解决方法。
原答案
Kotlin 中的每个 suspend
方法都通过 coroutines design document 中解释的 CPS 转换在 JVM 上表示。 Java 反射并不知道它,Kotlin 反射目前也没有提供方便的方法来执行挂起函数的调用。
您必须自己通过辅助函数调用 CPS 转换。为此,我建议实施以下助手:
import java.lang.reflect.Method
import kotlin.coroutines.experimental.intrinsics.*
suspend fun Method.invokeSuspend(obj: Any, vararg args: Any?): Any? =
suspendCoroutineOrReturn { cont ->
invoke(obj, *args, cont)
}
现在,如果您在代码中将 invoke
替换为 invokeSuspend
,那么它将按预期工作。
您可以使用 Kotlin 反射并按如下方式调用 suspend
:
suspend fun Any.invokeSuspendFunction(methodName: String, vararg args: Any?): Any? =
this::class.memberFunctions.find { it.name == methodName }?.also {
it.isAccessible = true
return it.callSuspend(this, *args)
}
有一个协程块可以 运行s 挂起函数。
但是我通过invoke
通过反射调用函数。这是java风格的调用,显然简单的调用是行不通的。
有没有办法 运行 异步反映方法?如何等待这个方法?
import kotlin.coroutines.experimental.*
class TestClass(val InString: String) {
suspend fun printString() {
println(InString)
}
}
fun launch(context: CoroutineContext, block: suspend () -> Unit) =
block.startCoroutine(StandaloneCoroutine(context))
private class StandaloneCoroutine(override val context: CoroutineContext): Continuation<Unit> {
override fun resume(value: Unit) {}
override fun resumeWithException(exception: Throwable) {
val currentThread = Thread.currentThread()
currentThread.uncaughtExceptionHandler.uncaughtException(currentThread, exception)
}
}
fun main(args: Array<String>) {
launch(EmptyCoroutineContext) {
val a = TestClass("TestString");
for (method in a.javaClass.methods) {
if (method.name == "printString")
method.invoke(a) // Exception in thread "main" java.lang.IllegalArgumentException: wrong number of arguments
}
}
}
更新
由于 Kotlin 1.3 反射原生支持通过 KFunction.callSuspend
和 KFunction.callSuspendBy
调用挂起函数,因此不再需要上述解决方法。
原答案
Kotlin 中的每个 suspend
方法都通过 coroutines design document 中解释的 CPS 转换在 JVM 上表示。 Java 反射并不知道它,Kotlin 反射目前也没有提供方便的方法来执行挂起函数的调用。
您必须自己通过辅助函数调用 CPS 转换。为此,我建议实施以下助手:
import java.lang.reflect.Method
import kotlin.coroutines.experimental.intrinsics.*
suspend fun Method.invokeSuspend(obj: Any, vararg args: Any?): Any? =
suspendCoroutineOrReturn { cont ->
invoke(obj, *args, cont)
}
现在,如果您在代码中将 invoke
替换为 invokeSuspend
,那么它将按预期工作。
您可以使用 Kotlin 反射并按如下方式调用 suspend
:
suspend fun Any.invokeSuspendFunction(methodName: String, vararg args: Any?): Any? =
this::class.memberFunctions.find { it.name == methodName }?.also {
it.isAccessible = true
return it.callSuspend(this, *args)
}