Модернизация и Moshi: Получите запрос с закрытым классом и обобщениями - возможно ли это? - PullRequest
0 голосов
/ 29 мая 2020

У меня есть запечатанный класс для обработки состояния моих ответов на модернизацию. Его члены принимают тип generi c. Я хотел бы получить Retrofit, чтобы иметь возможность вернуть правильный объект, но я застрял на этой ошибке: Unable to create converter for com.my.app.DataResult<?> - Cannot serialize abstract class com.my.app.DataResult

Это мой класс DataResult:

sealed class DataResult<out T> {

    data class Success<out T>(val data: T?) : DataResult<T>()

    data class Error<out T>(val code: Int? = null, val error: Exception? = null) : DataResult<T>()
    object NetworkError : DataResult<Nothing>()

    fun isSuccess() = this is Success<*>
    fun isError() = this is Error<*>
    fun data() = if (isSuccess()) (this as Success<T>).data else null
}

fun successResult() = DataResult.Success(null)
fun <T> successResult(data: T?) = DataResult.Success(data)
fun errorResult() = DataResult.Error<Nothing>(null)

Это остальная часть моей текущей реализации:

class NetworkClient(private val httpClient: HttpClient) {

    private val baseUrl: String = "some url"

    private val retrofit = Retrofit.Builder()
        .baseUrl(mockend)
        .addCallAdapterFactory(MyCallAdapterFactory())
        .addConverterFactory(MoshiConverterFactory.create())
        .client(httpClient.get())
        .build()

    private val apiService: ApiService = retrofit.create(StaApiService::class.java)
    suspend fun <T> sendGet(endPoint: EndPoint, input: String): DataResult<T> {
        val result = apiService.sendGetRequest<T>(endPoint.stringValue, queryMapOf(Pair("query", input)))

        when (result) {
            // do stuff here?
        }
        return result
    }
}

interface ApiService {

    @GET
    suspend fun <T> sendGetRequest(
        @Url url: String,
        @QueryMap parameters: Map<String, String>): DataResult<T>

    @GET
    suspend fun <T> sendGetListRequest(
        @Url url: String,
        @QueryMap parameters: Map<String, String>): DataResult<List<T>>
}
abstract class CallDelegate<TIn, TOut>(
    protected val proxy: Call<TIn>
) : Call<TOut> {
    override fun execute(): Response<TOut> = throw NotImplementedError()
    final override fun enqueue(callback: Callback<TOut>) = enqueueImpl(callback)
    final override fun clone(): Call<TOut> = cloneImpl()
    override fun cancel() = proxy.cancel()
    override fun request(): Request = proxy.request()
    override fun isExecuted() = proxy.isExecuted
    override fun isCanceled() = proxy.isCanceled
    abstract fun enqueueImpl(callback: Callback<TOut>)
    abstract fun cloneImpl(): Call<TOut>
}
class ResultCall<T>(proxy: Call<T>) : CallDelegate<T, DataResult<T>>(proxy) {
    override fun enqueueImpl(callback: Callback<DataResult<T>>) = proxy.enqueue(object : Callback<T> {
        override fun onResponse(call: Call<T>, response: Response<T>) {
            val code = response.code()
            val result: DataResult<T> = if (code in 200 until 300) {
                val body = response.body()
                DataResult.Success(body)
            } else {
                DataResult.Error(code)
            }

            callback.onResponse(this@ResultCall, Response.success(result))
        }

        override fun onFailure(call: Call<T>, t: Throwable) {
            val result: DataResult<Nothing> = if (t is IOException) {
                DataResult.NetworkError
            } else {
                DataResult.Error(null)
            }

            callback.onResponse(this@ResultCall, Response.success(result))
        }
    })

    override fun cloneImpl() = ResultCall(proxy.clone())
}
class ResultAdapter(
    private val type: Type
) : CallAdapter<Type, Call<DataResult<Type>>> {
    override fun responseType() = type
    override fun adapt(call: Call<Type>): Call<DataResult<Type>> = ResultCall(call)
}
class MyCallAdapterFactory : CallAdapter.Factory() {
    override fun get(
        returnType: Type,
        annotations: Array<Annotation>,
        retrofit: Retrofit
    ) = when (getRawType(returnType)) {
        Call::class.java -> {
            val callType = getParameterUpperBound(0, returnType as ParameterizedType)
            when (getRawType(callType)) {
                Result::class.java -> {
                    val resultType = getParameterUpperBound(0, callType as ParameterizedType)
                    ResultAdapter(resultType)
                }
                else -> null
            }
        }
        else -> null
    }
}

Приведенный выше код в значительной степени вдохновлен этим ответом на другой вопрос , но Я пытаюсь добавить в смесь Generics, поэтому мне не нужно вручную помещать каждый запрос в интерфейс. Возможно это или нет? Я пытался часами, также пытался создать адаптер для запечатанного класса, но безуспешно. Есть ли у кого-нибудь хороший ресурс, как это можно сделать?

Как вы также можете видеть в коде, я хотел бы также иметь возможность получать списки. Любые советы здесь тоже очень ценны.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...