KaduCmK
KaduCmK

Reputation: 33

Share fields between subclasses of a sealed class

I'm relatively new at Kotlin, and currently learning about sealed classes and interfaces. One concept I've been trying to grasp is how to make some fields in the base class shared between the subclasses (let's say, an Id field). Right now I currently have this:

sealed class Emprestimo(open val id: String, open val value: Double) {
  data class Credito(override val id: String, override val value: Double) : Emprestimo(id, value)
  data class Debito(override val id: String, override val value: Double) : Emprestimo(id, value)
}

Is this the correct way of implementing extended fields?

Upvotes: 0

Views: 70

Answers (1)

tyg
tyg

Reputation: 14800

It depends on what you mean by "share".

Let's assume we create the following objects:

val credit1 = Emprestimo.Credito("one",   1.0)
val credit2 = Emprestimo.Credito("two",   2.0)
val debt3   = Emprestimo.Debito ("three", 3.0)
val debt4   = Emprestimo.Debito ("four",  4.0)

Then none of the objects will share any data with any of the other objects. That's quite obvious since the objects must be created providing these values, and as you can see above, they are all different. In this sense, no, nothing is shared with the base class.


But maybe you mean something different. Let's put all the objects in a list and iterate it:

val list: List<Emprestimo> = listOf(credit1, credit2, debt3, debt4)
list.forEach {
    val id = it.id
    val value = it.value
}

Although the list only knows about the base type Emprestimo, we can still access all objects' id and value property, because their declaration is shared with the base class: You explicitly override the properties. In this sense, Credito and Debito share the declaration of their properties, because they both override Emprestimo's properties. But keep in mind, the content will still be different for each.


If you actually want the subclasses to share the same content, then you need another declaration:

sealed class Emprestimo(open val id: String, open val value: Double) {
    val fixed = 42
    
    data class Credito(override val id: String, override val value: Double) : Emprestimo(id, value)
    data class Debito(override val id: String, override val value: Double) : Emprestimo(id, value)
}

The new property fixed is now shared by all subclasses. Since it is not open, it cannot be overridden and will always be 42. Keep in mind, though, that each object (f.e. credit1, credit2, debt3, debt4) has its own property fixed. By the declaration it is guaranteed to always be 42 for all of them, but still they have their own property.


And finally, if you want there to be only a single value that is shared by all instances, i.e. you want to share the identical value, then you need to move that out to a companion object:

sealed class Emprestimo(open val id: String, open val value: Double) {
    data class Credito(override val id: String, override val value: Double) : Emprestimo(id, value)
    data class Debito(override val id: String, override val value: Double) : Emprestimo(id, value)

    companion object {
        const val constant = 1337
    }
} 

The companion object is independent from class instances and only exists once for the class, so constant truly only exists once. Since it isn't a part of the subclasses or its instances anymore, you need to access it on the class itself:

val constant = credit1.constant    // Error "Unresolved reference: constant"
val constant = Emprestimo.constant // Works as expected

But in the body of a subclass's definition it can be accessed like it would belong to that class:

data class Credito(override val id: String, override val value: Double) : Emprestimo(id, value) {
    fun doSomething() {
        val newValue = constant * 3
    }
}

All in all, this is not a question abour right or wrong or best practices, it only depends on what you want to achieve.

Upvotes: 0

Related Questions