Back

Sealed EnumA Kotlin annotation processor for replacing enum classes with sealed classes of objects.

Why we built it.

In Kotlin, sealed classes are more powerful and more flexible than enums, but enums can still do some things that sealed classes can't. We recognized that a sealed class of objects is strictly more powerful than enums, given a bit of repeated boilerplate code:

sealed class Alpha {
    object Beta : Alpha()
    object Gamma : Alpha()

    @GenSealedEnum
    companion object
}

println(Beta.ordinal) // 0
println(Beta.name) // "Beta"
println(Alpha.values) // [Alpha$Beta@491cc5c9, Alpha$Gamma@74ad1f1f]
println(Alpha.valueOf("Beta")) // Alpha$Beta@491cc5c9

sealed-enum generates that boilerplate code automatically, so we can replace enums and gain all of the benefits of using sealed classes without the tedious and error-prone maintenance of boilerplate.

How does it work?

If the companion object of any sealed class of objects is annotated with @GenSealedEnum, sealed-enum will generate code that allows using the sealed class like an enum. Extension properties are generated for the objects (ordinal and name), as well as the sealed class's companion object (values, valueOf). These extension properties are backed by an object implementing SealedEnum, which provides a generic interface that is more convenient that generically handling normal enum classes.

Additional features include specifying the traversal order of sealed class hierarchies, creating a SealedClass object from normal enum classes, and generating isomorphic enum classes for sealed enums.

How do I use it?

0) Add dependencies to Gradle via Jitpack

plugins {
    kotlin("kapt")
}

repositories {
    maven(url = "https://jitpack.io")
}

dependencies {
    implementation("com.github.livefront.sealed-enum:sealedenum:VERSION")
    kapt("com.github.livefront.sealed-enum:sealedenumprocessor:VERSION")
}

See the GitHub project for the latest version information.

1) Annotate the companion object of a sealed class with @GenSealedEnum

sealed class Alpha {
    object Beta : Alpha()
    object Gamma : Alpha()

    @GenSealedEnum
    companion object
}

Use the generated extension methods:

println(Beta.ordinal) // 0

println(Alpha.values) // [Alpha$Beta@491cc5c9, Alpha$Gamma@74ad1f1f]

The SealedEnum implementation can be retrieved with Alpha.sealedEnum, and backs the other extensions properties and methods:

object AlphaSealedEnum : SealedEnum<Alpha> {
    override val values: List<Alpha> = listOf(
        Alpha.Beta,
        Alpha.Gamma
    )

    override fun ordinalOf(obj: Alpha): Int = when (obj) {
        Alpha.Beta -> 0
        Alpha.Gamma -> 1
    }

    override fun nameOf(obj: AlphaSealedEnum): String = when (obj) {
        Alpha.Beta -> "Alpha_Beta"
        Alpha.Gamma -> "Alpha_Gamma"
    }

    override fun valueOf(name: String): AlphaSealedEnum = when (name) {
        "Alpha_Beta" -> Alpha.Beta
        "Alpha_Gamma" -> Alpha.Gamma
        else -> throw IllegalArgumentException("""No sealed enum constant $name""")
    }
}

See the GitHub project for more detailed usage instructions.

We are Livefront.

Our team creates beautiful mobile apps for people in motion. We work every day with Fortune 500 companies and startups alike to build custom software for phones, tablets, mobile payment wallets, and wearables like watches and glasses. We care deeply about quality, and we love what we do.