1

A generic class for holding network request result

sealed class Result<out T : Any?> {
    data class Success<out T : Any?>(val data: T) : Result<T>()
    data class Error(val message: String, val exception: Exception? = null) : Result<Nothing>()
}

A generic function for encapsulating network result into Result. It is called from a repository and passes a retrofit2 api call as an input parameter

suspend fun <T: Any?> request(method: Call<T>): Result<T> {
    return withContext(Dispatchers.IO) {
        try {
            val response = method.awaitResponse()   // Retrofit2 Call
            if (response.isSuccessful)
                Result.Success(response.body())
            else
                response.getErrorResult()
        } catch (e: Exception) {
            Result.Error(e.message.orEmpty(), e)
        }
    }
    // Type mismatch.
    // Required: Result<T>
    // Found: Result<T?>
}

It is called like this

interface Webservice {
    @GET("data")
    fun getData(): Call<Data>   // Retrofit2
}

suspend fun getData(): Result<Data> {
    return request(webservice.getData())
}

Why does it infer the result as type T? but not T?

yaugenka
  • 2,602
  • 2
  • 22
  • 41

1 Answers1

1

The problem is this line:

Result.Success(response.body())

body is marked with Nullable, so when ported to Kotlin, response.body() returns a T?, rather than T. See here for how this works.

Therefore, the type parameter for Result.Success is inferred to be T?, and so the expression above creates a Result<T?>. Note that the T in Result<T?> refers to the type parameter of request.

Sweeper
  • 213,210
  • 22
  • 193
  • 313
  • `since you have checked isSuccessful, body shouldn't be null`, it can be null, when the server returns OK with null value, but this does not matter here. I'm just confumed, why does it make it 'T?' while 'T' already allows a nullable value? – yaugenka Jan 08 '22 at 16:40
  • @yaugenka You might be confused because these two `T`s are not the same. `T` (the type parameter for `Result.Success`) allows a nullable type, so it is inferred to be a nullable `T` (the type parameter for `request`), aka `T?`. – Sweeper Jan 08 '22 at 16:46
  • @yaugenka Or are you asking why does `T?` exist at all, when `T: Any?`? Well, as I have said in my previous answer, both nullable and non-nullable types satisfy `T: Any?`, so `T?` represents a `T` that is definitely nullable, and that is what the designers of the Retrofit API wanted `body` to return. – Sweeper Jan 08 '22 at 16:56
  • I think, I have understood it now. If the `request` is called with a method which returns a non-nullable type, like in the example provided, then `T` will be inferred as non-nullable and that would contradict the nullability of `body`. Thanks! – yaugenka Jan 08 '22 at 17:04