More type fun: ```fun Serializable.toJson(): Strin...
# getting-started
r
More type fun:
Copy code
fun Serializable.toJson(): String = Json.encodeToString(this)
This doesn't seem to match my type I marked as
@Serializable
🤔
r
Applying Serializable to the Kotlin class instructs the serialization plugin to automatically generate implementation of KSerializer for the current class, that can be used to serialize and deserialize the class.
which is different from extending the
Serializable
interface 🙂
🥇 1
j
@Serializable
is different from the
Serializable
interface
🥈 1
r
Ah, duh, thanks.
j
I wish there was a way to check this at compile time, though :/
r
I wish I didn't have serialization crashes at runtime because I didn't specify all subtypes 😕
j
Could you please elaborate? Is this because you're not using sealed classes?
r
Using sealed, sometimes I'm getting runtime errors because I forgot to annotate one of the types used in a field (maybe, memory is hazy)
j
No, non-serializable fields are reported at compile time
r
Weird, how'd I manage that runtime error 🤔
j
Runtime errors should only happen if you're using a non serializable class in the top-level call to
encodeToX
, AFAIK
r
Ah, that might have been it
j
So yeah, that I wish was picked up by the compiler. Maybe by generating some
KxSerializable
interface or something
But hey, if you're hardcore, technically you can have your own interface, and then have a test that ensures via reflection that all of its implementations are
@Serializable
r
Isn't reflection runtime? 🤔
j
I mean in your tests
r
Then I can just run selfie anyway, that'll give me the exceptions in case of a lack of
@Serializable
j
What's selfie?
r
j
I don't see immediately how that would help
r
It'll trigger exceptions in case of no
@Serializable
, but ain't much more than that
Doing reflection to check isn't gaining you anything, it'll throw anyway
j
I think you misunderstood me, you can write a single test in your project that will check that all the classes that implement your custom interface are in fact serializable. Then you can define the function that you had in your original post, but as an extension of your custom interface. And now you're guaranteed at compile time that all is good, and you don't need any specific test for each of your classes. The test is just here to catch if you forgot the @serializable on a class that you made implement your custom interface.
r
That just means I'd have to add a custom interface for each class/interface, right? Sounds like extra work for no gain, I'd rather write a tree walker which verifies that any type used in an
encodeX
has
@Serializable
, although that's probably quite some ksp magic
j
You can't write such a check, though. Functions can be extracted, generics can be used, etc. You need real type-checking for this, hence the interface. By the way, I wonder if you even need the test if you make the interface sealed and serializable itself 🤔
r
Can
ksp
access the type-checked tree?
Or is the specialization pass later?
j
I'm not sure about how it works. But without an interface there is no type to check for anyway. My point is that you can't rely on
@Serializable
alone:
Copy code
fun myCustomEncode(thing: Any) {
    Json.encodeToString(thing)
}
How do you want to check that this is called only with instances of classes marked
@Serializable
? Calls to this function could be in different modules, even. If you use an interface, you can enforce:
Copy code
// in your core utils
fun MySerializable.toJson(): String = Json.encodeToString(this)

// anywhere in your business code
fun myFunThatEncodes(thing: Any) {
    thing.toJson() // type error: expected MySerializable but got Any
}
That's what you gain from the interface. Then the last hole to plug is how to make sure all implementations of the interface actually are marked
@Serializable
and that can be done by a reflection test or by KSP, whichever you prefer
r
Ok, now the chain makes sense, thanks. Probably KSP, so it's compile-time.
👍 1