I’m new with Kotlin. I’m reading a book and a sealed class is displayed there as an “extension” of Enum. I can’t see the similarity between them. The way I see the things, Sealed class is more related to inheritance, because each class can inherit from it and to add function and properties to it For example:
sealed class messageType class MessageSuccess (var msg: String) : MwssageType() class MessageFailure (var msg: String, var e: Exeception) : MwssageType()
I don’t see here values like we have in Enum, only kink of inheritance. Can someone explain me what is the imagine between Enum and Sealed that i can’t find? Maybe the power of it is when using it with when expression?
Advertisement
Answer
I think what the documentation means by extension, is not actually extending enums, but a tool like enums with more power as it can hold state. lets take a look at your example with enums.
sealed class SealedMessageType class MessageSuccess (val msg: String) : SealedMessageType() class MessageFailure (val e: Exeception) : SealedMessageType() enum class EnumMessageType { Success, Failure }
and now if you use enums you have:
val enumMessageType: EnumMessageType = callNetwork() when(enumMessageType) { EnumMessageType.Success -> { TODO() } EnumMessageType.Failure -> { TODO() } }
here when you use enums you can’t retrieve the data of your result from enums, and you need to get the message or error with some other variable. the only thing you can get is the type of the result without its state. but with sealed classes:
val sealedMessageType: SealedMessageType = callNetwork() when(sealedMessageType) { is MessageSuccess -> { println(sealedMessageType.msg) } is MessageFailure -> { throw sealedMessageType.e } }
the IDE can smart cast your result and you can get the state of your result(if success the message, if failure the exception). this is what the doc means by extension.
but overall you are right, sealed class is about inheritance. in fact, a sealed class is nothing but an abstract class that has a private constructor and can not be instantiated. let’s look at the decopiled java code:
@Metadata( mv = {1, 4, 0}, bv = {1, 0, 3}, k = 1, d1 = {"u0000u0014nu0002u0018u0002nu0002u0010u0000nu0000nu0002u0018u0002nu0002u0018u0002nu0000b6u0018u00002u00020u0001Bu0007bu0002¢u0006u0002u0010u0002u0082u0001u0002u0003u0004¨u0006u0005"}, d2 = {"Lcom/example/customview/SealedMessageType;", "", "()V", "Lcom/example/customview/MessageSuccess;", "Lcom/example/customview/MessageFailure;", "app"} ) public abstract class SealedMessageType { private SealedMessageType() { } // $FF: synthetic method public SealedMessageType(DefaultConstructorMarker $constructor_marker) { this(); } }
@Metadata( mv = {1, 4, 0}, bv = {1, 0, 3}, k = 1, d1 = {"u0000u0012nu0002u0018u0002nu0002u0018u0002nu0000nu0002u0010u000enu0002bu0004u0018u00002u00020u0001Bru0012u0006u0010u0002u001au00020u0003¢u0006u0002u0010u0004Ru0011u0010u0002u001au00020u0003¢u0006bnu0000u001au0004bu0005u0010u0006¨u0006u0007"}, d2 = {"Lcom/example/customview/MessageSuccess;", "Lcom/example/customview/SealedMessageType;", "msg", "", "(Ljava/lang/String;)V", "getMsg", "()Ljava/lang/String;", "app"} ) public final class MessageSuccess extends SealedMessageType { @NotNull private final String msg; @NotNull public final String getMsg() { return this.msg; } public MessageSuccess(@NotNull String msg) { Intrinsics.checkNotNullParameter(msg, "msg"); super((DefaultConstructorMarker)null); this.msg = msg; } }
here you can see that the SealedMessageType
is in fact an abstract class. the only difference between an abstract class and a sealed class is that the compiler generates some metadata for sealed classes and can warn you about the missing branches when you use the when
keyword which can’t be done using abstract classes. you can see in the above code that the SealedMessageType
class metadata contains both MessageSuccess
and MessageFailure
as child classes, and MessageSuccess
metadata also contains SealedMessageType
as a parent. there is no such metadata if you use abstract classes.
if you use this simple trick, the compiler gives you an error if you miss any branches and IDE can help you implement missing branches using Alt+Enter
. the trick is to define an exhaustive extension function:
fun main() { when(callNetwork()) { // error: when' expression must be exhaustive, add necessary 'is MessageSuccess', 'is MessageFailure' branches or 'else' branch instead }.exhaustive() } fun Any?.exhaustive() = this fun callNetwork(): SealedMessageType { TODO() }