https://kotlinlang.org logo
#getting-started
Title
# getting-started
c

Colton Idle

03/09/2023, 6:16 PM
I always get stuck between sealed classes and enums. What I'm having trouble doing now is modelling a type that gets passed back in a callback. i.e. Book with id, Magazine with id, Record with id, artist, and a boolean. Would a sealed class like this be the best way to do it?
Copy code
sealed class CallbackItemType(){
        class Book(val id: Long) : CallbackItemType()
        class Magazine(val id: Long) : CallbackItemType()
        class Record(val id: Long, val artist: String, val enabled: Boolean) : CallbackItemType()
}
Technically speaking, I don't think an enum would even be able to have that sort of data modelled?
c

Casey Brooks

03/09/2023, 6:30 PM
What you’re doing is correct, a sealed class is what you need. You cannot create new instances of enums, they are constants and each enum case is a singleton. Since you need to supply each CallbackItemType with different values, you need the sealed class so you can create new instances with different values in them Conceptually, an enum is kind of like this:
Copy code
sealed class CallbackItemType(){
    object Book : CallbackItemType()
    object Magazine : CallbackItemType()
    object Record : CallbackItemType()
}
c

Colton Idle

03/09/2023, 6:35 PM
awesome. just for the sake of learning. in java 7 (android land a few years ago), was there no way of modelling this type of thing in java?
c

Casey Brooks

03/09/2023, 7:08 PM
That’s right, you can’t restrict a type hierarchy in Java code before Java 17. Enums are constants and can be used in
switch
statements, but classes are not allowed. Java 17 introduces both sealed classes and pattern matching for
switch
, which gives Java
switch
statements similar functionality to Kotlin’s
when
, but a bit more flexible and more powerful. But before 17, enums were the only real way to have limited type-safe code.
f

Francesc

03/09/2023, 7:26 PM
if you
id
is common, move it to the base class, otherwise make the base class an
interface
actually, make it an interface regardless,
Copy code
sealed interface CallbackItemType{
    val id: Long
    
    class Book(override val id: Long) : CallbackItemType
    class Magazine(override val id: Long) : CallbackItemType
    class Record(override val id: Long, val artist: String, val enabled: Boolean) : CallbackItemType
}
use a class if you have some common logic in the base class
c

Casey Brooks

03/09/2023, 7:32 PM
I would actually recommend not moving
id
to the base class for this example. If there’s nothing you will do “common” with that ID, there’s no need to add that extra boilerplate. In other words, if you never need to do something like
fun CallbackItemType.doAThing() { doSomethingCommonWithId(id) }
, it’s unnecessary abstraction that actually makes things harder to maintain in the long-term. Using a
sealed interface
instead of
sealed class
is a good idea though. Doesn’t really change much other than making the class slightly “lighter”, since inheritance is slightly “heavier” at runtime than implementation, but it does make the code a slight bit shorter since you can omit the parentheses on
: CallbackItemType
👍 2
2 Views