Help with generics, reified types and inheritence (using HttpClient.get<T>)

I am trying to write a model object and repository that is shared between the jvm backend and the js front end. I am using ktor, kodein and kotlinx.serialization with json between the backend and the front end.

I am having trouble with generics and reified types with respect to inheritence.

Short summary: HttpClient.get<T>("url") wants T to be a real type or it wants the type to be reified. How can I implement this in a generic (with inherited class/interface) way, or work around it?

Longer description:
I have a BaseRepository that looks like this:

interface BaseRepository<T: WithIdAndVersion> {
    /**
     * Finds the object given by the id and returns it. If it is not found, null is returned.
     */
    suspend fun find(id: Long): T?
etc

I then have a RemoteRepository on the js side and a DbRepository on the jvm side. The RemoteRepository one looks like:

open class RemoteRepository<T: WithIdAndVersion>(val classUrl: String) : BaseRepository<T> {
    @Suppress("OVERRIDE_BY_INLINE")
    final override suspend inline fun <reified T> find(id: Long): T? {
        HttpClient(Js) {
            install(JsonFeature) {
                serializer = defaultSerializer()
            }
        }.use {
            return try {
                it.get<T>("$baseUrl/$id")
            } catch(e: BadResponseStatusException) {
                if (e.statusCode == HttpStatusCode.NotFound) {
                    null
                } else {
                    throw e
                }
            }
        }
    }
etc

This doesn’t compile because the find function in the base interface is not reified, and it is not allowed to be because reified needs the function to be inlined and inlined can’t be on virtual methods. (If I call it find2, i.e. not override it, it works, but I am using Kodein for injection so implementing the base interface is important)

How I got to all the reifiing, inlining, etc in the first place was because of the HttpClient.get<T>("url"). It wants a real type or it wants the type to be reified.

So how can I create a generic find function as above, or work around the HttpClient.get and get a real kotlin type back (rather than a pseudo javascript JSON created class).

Not sure how best to proceed… any pointers appreciated.

Just coming back to this problem again. How would I implement this in a generic way, in particular, the HttpClient.get?

What I have done since is made the RemoteRepository take a serializer:

open class RemoteRepository<T: WithIdAndVersion>(val classUrl: String, val kSerializer: KSerializer<T>? = null) : BaseRepository<T> {

Then, in the find I go

return try {
    if (kSerializer != null) {
        Json.parse(kSerializer, it.get("$baseUrl/$id"))
    } else {
        println("kSerializer was null")
        null
    }
...

So, have found a work around… is this the best (probably not :slight_smile:) solution, or can the original request be satisfied using generics somehow?

I am doing something similar with passing in the serializer, is there any way to just get it from T within RemoteRepository?

I just went back to check if there were any changes to the code mentioned above, and it is still looking the same, so looks like I, at least, did not find another way of doing it. :slight_smile:

i was able to slap some toJson methods on my classes via interface, but i feel dirty passing the serializer in along while defining T, lol…

override fun toJson(): String {
    return Json.stringify(serializer(), this)
}