Search code examples
javakotlinlanguage-interoperability

How to Override a suspend function in java class


Consider the following interface in kotlin:

LoginRepository.kt

interface LoginRepository {
    suspend fun login(): LoginResponse
}

LoginRepo.java

class LoginRepo implements LoginRepository {
    public Object login(@NonNull Continuation<? super LoginResponse> $completion) {
        api.login((result) -> {
            ContinuationUtilsKt.resumeContinuationWithSuccess($completion, result);
        });

        return null;
    }
}

ContinuationUtils.kt

fun <T> resumeContinuationWithSuccess(cont: Continuation<T>, value: T) {
    cont.resumeWith(Result.success(value))
}

I've attempted to drill down the code to its essential parts, i.e. a suspend function that is overridden in the java class makes an API call and returns a success or failure continuation using the continuation object and returns null.

However, the method LoginRepository.login when called returns null.

The overridden method signature in LoginRepo is generated by the IDE.

Am I supposed to return some object instead of null? Or something else that I'm missing.


Solution

  • I really don't think you're supposed to do this. The functions and classes used to implement it in Kotlin are internal/private and hidden from the Java side.

    Basically, you need to intercept the original Continuation and resume the new returned Continuation with your return value. Then return Intrinsics.COROUTINE_SUSPENDED to indicate that you are not synchronously returning a value. If the return value is anything besides Intrinsics.COROUTINE_SUSPENDED, then I think it assumes you are directly returning the declared return value of the suspend function.

    While this code may work, it probably doesn't handle all the edge cases, and it probably won't provide helpful stack traces in the event of a crash. The standard library implementation is far more complicated.

    class LoginRepo implements LoginRepository {
        public Object login(@NonNull Continuation<? super LoginResponse> $completion) {
            Continuation<? super LoginResponse> cont = IntrinsicsKt.intercepted($completion);
    
            api.login((result) -> {
                ContinuationUtilsKt.resumeContinuationWithSuccess(cont, result);
            });
    
            return IntrinsicsKt.getCOROUTINE_SUSPENDED();
        }
    }