tl;dr; What's the easiest way to convert
data class User(val organization: Organization)
to
data class UserNullable(val organization: Organization?)
so I have both available as options?
Long story:
I'm writing Spring Boot integration tests for my Kotlin REST app and running into difficulties deserializing from JSON. For example, I have a User
class and it has a non-nullable Organization
inside it. But sometimes I want to return a user without their organization, so I just don't load the organization in Hibernate and it automatically gets serialized to null
by Jackson.
In Typescript there's a feature called Partial
that lets you create a version of a class where all its fields are considered nullable. What would be the easiest way to do that with Kotlin? I don't want to destroy the null-safety of all my entity classes. I also don't want to create an entire superfluous DTO layer.
I know I could make my classes all use late init var
instead of the constructor parameter val
s, but that would remove the immutability of the classes, which I also think is valuable.
One possible solution for the minimal example you have given would be to introduce a bounded type parameter for the value, like so:
class UserGeneric<T : Organization?>(val organization: T)
typealias User = UserGeneric<Organization>
typealias UserNullable = UserGeneric<Organization?>
But that would get very tedious and unreadable once you have (data) classes with lots of fields, because every single one would need an associated type parameter. So the short answer is: No, there's no such thing as TypeScript's Partial
in Kotlin.
You will have to resort to actually write these classes twice. But if you do that, one thing that can help you is the fact that nullable types in Kotlin are always considered to be the supertypes of their non-nullable counterparts. Which means, you can develop a helpful inheritance structure between your nullable and non-nullable User
classes:
open class UserNullable {
open val organization: Organization? // initialize or write constructor(s)
}
class User : UserNullable() {
override val organization: Organization // initialize or write constructor(s)
}
which means that now all of your User
instances are assignable to the UserNullable
type. For example, you can write a function that takes UserNullable
as a parameter, but you can also pass a User
.
You might end up designing the inheritance hierarchy a bit nicer, for example by specifying your fields in the primary constructor or somehow using interfaces instead. The above snippet only serves as a demonstration for how that inheritance structure could work.
Finally, your last option is to just use the nullable version everywhere. It's definitely a bit more tedious but Kotlin's nullability handling tools (safe calls, elvis, non-null assert, smart casts, ...) are quite powerful and it might not be so bad after all.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With