Hi, I noticed a common problem when modeling using...
# announcements
a
Hi, I noticed a common problem when modeling using Sealed classes and Data classes. In my projects, this approach is too common and we noticed a boilerplate problem when we need to modify the content using the copy method of data classes. Currently, Kotlin does not provide a way to restrict the Sealed classes implementations to Data classes, so if you want to use copy method, you need to check for all concrete data classes and it is a pain in the ass. Imagine the next scenario:
Copy code
sealed class MyModel {
        abstract val name: String
        abstract val title: String
}
data class Model1(
            override val name: String,
            override val title: String,
) : MyModel()
...
data class Model100(
            override val name: String,
            override val title: String,
) : MyModel()
If you want to modify the title, for example, adding the environment where you are running your code could be similar to:
Copy code
fun MyModel.addEnvironmentToTitle(env: String) = when (this) {
	is MyModel1 -> copy(title = title + env)
	... -> // Same boilerplate here
       is MyModel100 -> copy(title = title + env)
}
It is a simple example but you could have more complex code and it becomes unmanageable. Does anyone know if we will have in the future an option to restrict Sealed classes implementation to Data classes? With that restriction previous method would be:
fun MyModel.addEnvironmentToTitle(env: String) = copy(title = title + env)
d
Wouldn't this only be possible if all subclasses also have the exact same properties? Just put your
title
as a property on the common class. That's what inheritance is for.
s
@Dico Alas, no.. MyModel is not a data class and therefore the compiler doesn't know about the auto-generared
copy
method.
Something like a 'data interface' would help in this use-case :-)
l
If you don't mind using reflection you could do something like this
Copy code
inline fun <reified T: MyModel> T.addEnvironmentToTitle(env: String): T =  when {
    T::class.isData -> T::class.memberFunctions.first{ it.name == "copy"}.call(this, this.title, this.title + env) as T
    else -> this
}
a
+1 @streetsofboston It would be nice
@Lawik Would be nice does not require reflection for copy a object 😛. Thanks for your suggestion but I do not consider reflection as a option in prod.
@Dico The problem is with copy method as @streetsofboston said
s
And possibly the
componentXXX
methods as well.
s
We can't have
copy
in
MyModel
due to https://kotlinlang.org/docs/reference/compatibility-guide-13.html#data-class-overriding-copy , but we can have a
copy
-like abstract fun in
MyModel
.
a
Yes, that was my approach but you still need to implement it in all implementations...
r
It's important to note that data classes do not generate a no-args
copy
function. The generated function will have the same signature as the constructor, it just conveniently defines defaults for you. As a result, it makes no sense to require classes to be
data
as there is no common code among data classes.