1

I have a Kotlin class with two generic parameterized types, but when I try to use it, I get this error: "Only KClass supported as classifier, got K". How can I fix it without changing client code (main method)?

Code:

import kotlinx.serialization.decodeFromString
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
import java.io.File

class KeyValueStore<K, V>(filePath: String) {
    val dbFile = File(filePath)

    fun read(): Map<K, V> {
        if (!dbFile.exists()) {
            return mapOf()
        }

        val content = dbFile.readText()
        return Json.decodeFromString(content)
    }

    fun write(data: Map<K, V>) {
        val content = Json.encodeToString(data)
        dbFile.writeText(content)
    }
}

fun main() {
    val s = KeyValueStore<String, Int>("test.db")
    s.write(mapOf("a" to 1))
}

Exception:

Exception in thread "main" java.lang.IllegalStateException: Only KClass supported as classifier, got K
    at kotlinx.serialization.internal.Platform_commonKt.kclass(Platform.common.kt:102)
    at kotlinx.serialization.SerializersKt__SerializersKt.serializerByKTypeImpl$SerializersKt__SerializersKt(Serializers.kt:78)
    at kotlinx.serialization.SerializersKt__SerializersKt.serializer(Serializers.kt:59)
    at kotlinx.serialization.SerializersKt.serializer(Unknown Source)
    at kotlinx.serialization.SerializersKt__SerializersKt.builtinSerializer$SerializersKt__SerializersKt(Serializers.kt:96)
    at kotlinx.serialization.SerializersKt__SerializersKt.serializerByKTypeImpl$SerializersKt__SerializersKt(Serializers.kt:84)
    at kotlinx.serialization.SerializersKt__SerializersKt.serializer(Serializers.kt:59)
    at kotlinx.serialization.SerializersKt.serializer(Unknown Source)
    at utils.KeyValueStore.write(KeyValueStore.kt:97)
    at utils.KeyValueStoreKt.main(KeyValueStore.kt:91)
    at utils.KeyValueStoreKt.main(KeyValueStore.kt)

This problem doesn't happen when I replace all generic type parameters with fixed types like String for K, and Int for V.

It probably has something to do with the need for reified types, and I tried something complex like this below, but I still get the same error:

class KeyValueStore2<K: Any, V: Any> @PublishedApi internal constructor(
    private val classK: KClass<K>,
    private val classV: KClass<V>
) {
    val dbFile = File("test.db")

    companion object {
        @JvmStatic
        @JvmName(name = "create")
        inline operator fun <reified K : Any, reified V: Any> invoke() =
            KeyValueStore2(K::class, V::class)
    }
    ...
}

Edit: this question is related to Deserialize generic object using Kotlin Serialization, but different in that my question is about a generic class instead of function. Generic classes are more complex and less flexible than standalone generic functions.

Devabc
  • 4,782
  • 5
  • 27
  • 40
  • Does this answer your question? [Deserialize generic object using Kotlin Serialization](https://stackoverflow.com/questions/64896720/deserialize-generic-object-using-kotlin-serialization) – mightyWOZ Aug 09 '21 at 17:33
  • @mightyWOZ That's more about generic functions, my question is for a generic class, which is a bit more complex. The solution cannot be applied directly at my problem. – Devabc Aug 09 '21 at 18:06
  • 1
    I think there are two ways to make it work. 1) use your KeyValueStore2 class, and call Json.encodeToString(MapSerializer(classK.serializer(), classV.serializer()), data). 2) just create and pass MapSerializer instance to write( ) method. But both are a little ugly. – R.h Aug 14 '21 at 04:14

0 Answers0