Sealed class inheritance and constructors

Hi,

I’m wondering what is the cleanest way to inherit arguments in a constructor of a sealed class

sealed class Person(val id: Long, val name: String) {
    class Developer(id: Long, name: String, val favoriteLanguage: String): Person(id, name)
    class Painter(id: Long, name: String, val paintingsSold: Long): Person(id, name)
}

Is there a way to avoid repeating the args in each case of the class?
Ideally, I would like to write this

sealed class Person(val id: Long, val name: String) {
    class Developer(val favoriteLanguage: String): Person
    class Painter(val paintingsSold: Long): Person
}

And then create instances with the arguments from Person either before or after the arguments of Developer and Painter.

4 Likes

No, there is no such syntax in Kotlin. You have to repeat the arguments.

But if you have an idea how that syntax might look like, you can write a proposal for that.

For any such solution, it probably would be good if it could also inherit default parameters. But I don’t really have good syntax either (sealed class or not).

This becomes a bigger problem with sealed data classes. We can’t make Developer and Painter as data classes, because their primary constructor must have only property parameters.

We can handle this by overriding property, but this looks ugly:

sealed class Person(open val id: Long)
data class Developer(override val id: Long) : Person(id)

Or we can use different names for properties (still ugly):

sealed class Person(val id: Long)
data class Developer(private val realId: Long) : Person(realId)

Or this:

sealed class Person {
   abstract val id: Long
}

data class Developer(override val id: Long) : Person()

Is there a better solution?

4 Likes

I use the last one.

3 Likes

Hello! Could you give me an advice about sealed class’es inheritance? What if a hierarchy has more than one deep? For example, we have a Person.Developer.SoftwareDeveloper.JavaDeveloper? Do you see any disadvantages of this sealed class usage?
Thank you!

I also ran into these problems several times while working with sealed classes. The ergonomy of sealed classes in general is not yet there where it should be but I’m sure that it can be improved over time.

I think that the following syntax could work in general:

sealed class Person(val id: Long, val name: String) {
    class Developer constructor super : Person(...)
    class Painter constructor super : Person(...)
}

The constructor super informs the compiler that we want to inherit the constructor in its full glory (hence, including possible default values) and the ... tells the compiler that we want it to insert the right stuff here. I’m not sure if the ... poses a problem to the lexer, maybe something else is required here.

PS: Where can one make language design proposals and contribute to Kotlin?

3 Likes

Although I don’t have a concise syntax to propose it’s disturbing how verbose the current approach is.

while using the last approach, it doe not compile if there are multiple properties. for example

sealed class Person {
  abstract val id: Long,
  abstract val name: String
}
data class Developer(override val id: Long, override val name: String) : Person()

the compiler will complain with

Kotlin: Property getter or setter expected

what is the logic behind?

You have to put “id” and “name” inside Person :wink:

I’m wondering why compiler complains that “cannot access private method ” when I try to add new object outside that file

package com.example

sealed class ErrorType{
object HTTP: ErrorType()
}
object: INVALID: ErrorType() // this is ok

package com.other.pck

object INVALID_MAIL: ErrorType() // error

Sealed classes are designed that the children must be declared in the same file (or as nested class). That is the whole point of sealed. As such declaring children in a different package is against the design. The error message is poor though.

1 Like

@hui.zhang.jerry I’m not sure if you got an answer to your question, but you simply need to remove the , after abstract val id: Long in Person.

sealed class Person {
  abstract val id: Long  // Remove the comma
  abstract val name: String
}
// Now works as expected
data class Developer(override val id: Long, override val name: String) : Person()
3 Likes