Business
Jobs
  • About Us
  • Solutions
    • Job Postings
      Post your job and receive qualified candidates in 48h.
    • Candidate Assessments
      500+ technical and psychological tests, plus anti-fraud.
    • Headhunting
      Tailor-made executive search from start to finish.
    • Payroll + EOR
      Payroll dispersal and EOR across 15+ LATAM countries.
  • Pricing
  • Jobs

0

448
Views
Implementing generic method in interface that uses implementors class

I want a method on a parent Interface / Abstract Class that makes use of generics methods to pass in the class of the implementing class.

interface Domain {
    fun toJSON(): String { return Json.encodeToString(this) }
}

@Serializable
class User: Domain {
    val a: Int
}

This doesn't work since Json.encodeToString doesn't know the class of 'this'.

@Serializable seems to implement KSerializer so in theory I could require domain to descend from it, but that interface is templated. And marking the implementing class @Serializable doesn't seem to implement KSerializer until compile time so creates errors.

How do I implement this toJSON() method or tell Domain that its implementers must be @Serializable / KSerializer?

I have also tried:

interface Domain<T> {
    fun toJSON(): String { return Json.encodeToString(this) }
}

@Serializable
class User: Domain<User> {
    val a: Int
}

But this results in:

kotlin.IllegalStateException: Only KClass supported as classifier, got T

One additional complication in all this is that I'm attempting to do this in KMM.

over 4 years ago · Santiago Trujillo
3 answers
Answer question

0

It does not work, because encodeToString() resolves the type at compile time, not at runtime (it uses reified type).

Maybe there is a better way, but you can do this by acquiring a serializer manually, resolving the type at runtime:

fun toJSON(): String {
    val serializer = Json.serializersModule.serializer(this::class.createType())
    return Json.encodeToString(serializer, this)
}

Note this will only work on JVM. If you need to do this in KMM then be aware that multiplatform reflection is complicated and/or it needs some time to mature. kotlinx.serialization provides a way to do this, but there are warnings about possible inconsistent behavior between platforms:

@OptIn(InternalSerializationApi::class)
fun toJSON(): String {
    @Suppress("UNCHECKED_CAST")
    val serializer = this::class.serializer() as KSerializer<Any?>
    return Json.encodeToString(serializer, this)
}
over 4 years ago · Santiago Trujillo Report

0

Json.encodeToString uses a reified generic to access the class of the parameter. It means it uses the declared type of the parameter (known at compile time), which here is Domain.

The simplest approach would be to just use your own generic reified extension function to capture the declared class of the receiver more precisely:

interface Domain

inline fun <reified T : Domain> T.toJSON(): String = Json.encodeToString(this)

@Serializable
data class User(
    val name: String,
    val age: Int,
): Domain

fun main() {
    val user = User("Bob", 35)
    println(user.toJSON())
}

Note, however, that this suffers from the same problem: if you try to use this extension on a variable declared with Domain type, it still won't try to access the runtime type:

val user: Domain = User("Bob", 35)
println(user.toJSON()) // still a problem here

If you want to actually use the dynamic type, you can access the serializer dynamically instead like @broot mentioned in his answer.

over 4 years ago · Santiago Trujillo Report

0

@Serializable seems to implement KSerializer

That's not true. @Serializable is an annotation that instructs the annotation processor to generate (among other stuff) a serializer() method (for companion object), which returns an instance of KSerializer for this class. It doesn't add new interfaces for the class itself, so you can't tell the type system what it should check.

If a serializer won't be found, you'll get a runtime error:

import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json

class HasNoSerializableAnnotation

fun main() {
    // Compiles fine, but will throw runtime exception
    // kotlinx.serialization.SerializationException: Serializer for class 'HasNoSerializableAnnotation' is not found.
    Json.encodeToString(HasNoSerializableAnnotation()) 
}

That's intentional because it's possible to serialize instances of classes without a plugin-generated serializer (even if you don't pass custom serializer manually into encodeToXXX method as another parameter - via passing serializer (as contextual) in serializersModule).

How do I implement this toJSON() method or tell Domain that its implementers must be @Serializable

There is no way to do this. You can have this API, but it's runtime-errors prone:

interface Domain
inline fun <reified T : Domain> T.toJSON() = Json.encodeToString(this)

How do I implement this toJSON() method or tell Domain that its implementers must be KSerializer

Actually, you don't need whole KSerializer here - just its serialization part:

interface Domain
inline fun <reified T : Domain> T.toJSON(serializer: SerializationStrategy<T>) = 
    Json.encodeToString(serializer,this)
over 4 years ago · Santiago Trujillo Report
Answer question
Find remote jobs

Discover the new way to find a job!

Top jobs
Top job categories
Business
Post vacancy Pricing Sales
Legal
Terms and conditions Privacy policy
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Show me some job opportunities
There's an error!