简体   繁体   中英

Calling Coroutine functions from Java

I have written a small library for async testing in Kotlin with JUnit, which is based on coroutines with runBlocking scope. I would like to have at least some kind of interop with Java. My coroutine knowledge is limited, but my idea was that if I let Kotlin create the scope and only bridge suspend function calls in Java class, passing the Continuation created by Kotlin method to method it might work. Currently I have something like this construct

class AsyncTestLibrary {
    fun doSyncAction(){}
    suspend fun doAsyncAction(){}
}
// JAVA compatibility wrapper
fun runTest(lib: AsyncTestLibrary, testBody: suspend (lib: AsyncTestLibrary) -> Unit) =
    runBlocking {
        testBody(lib)
    }

And following usage in Java

@Test
public void test(){
    runTest(testLibraryInstance, (lib, continuation) -> {
        lib.doSyncAction();
        lib.doAsyncAction(continuation);

        return Unit.INSTANCE;
    });
}

Unfortunately this approach does not seem to be working, getting some strange behavior and classCastExceptions like kotlin.coroutines.jvm.internal.CompletedContinuation cannot be cast to class kotlinx.coroutines.internal.DispatchedContinuation

Is there something fundamental about coroutines that means this will never work in general, or is there some interop library / approach I am missing?

If you're OK with converting the suspend functions into Futures for use in Java, then you could do it like this.

You would need a CoroutineScope for running the coroutines that back up the Futures, and you would need a wrapper function for each suspend function that you want to use in Java.

You need to use the kotlinx-coroutines-jdk8 library for this.

In this example, I believe I have used Kotlin and Java conventional naming for the functions. "Async" is never used in the names of suspend functions, but is typically used in function/method names that do not synchronously return a result. "Sync" is never used in function or method names.

class AsyncTestLibrary {
    private val testScope = CoroutineScope(SupervisorJob()) + CoroutineName("TestScope")

    fun doActionA(){}
    suspend fun doActionB(): String { return "Test" }
    suspend fun doActionC(input: String) { println(input) }

    fun doActionBAsync() = testScope.future { doActionB() }
    fun doActionCAsync(input: String) = testScope.future { doActionC(input) }
}
@Test
public void test(){
    testLibraryInstance.doActionA();
    testLibraryInstance.doActionBAsync()
        .thenCompose(value -> testLibraryInstance.doActionBAsync(value));
}

I haven't personally done unit tests involving futures, so you'll have to look up how to wait for and assert results.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM