kotlinenumsinterfacesealedsealed-class

Kotlin - categorized subenums


I am trying to implement a Role class/interface/enum that I can use throughout my program. I want the roles to be somewhat categorized - I want some roles to be of type A, some roles to be of type B, and some roles to be part of multiple types. I want to be able to know all the roles from each type - so an enum/sealed class structure is the idea. I tried the following implementation -

sealed interface UserRole {
  val roleName: String
}

enum class RoleA(override val roleName: String): UserRole {
  A(roleName = "A"),
  B(roleName = "B"),
  C(roleName = "C"),
  D(roleName = "D");

  companion object {
    fun fromRoleName(roleName: String): RoleA? =
        values().singleOrNull { it.roleName == roleName }
  }
}

enum class RoleB(override val roleName: String, val secondParam: String): UserRole {
  A(roleName = "A", secondParam = "A"),
  E(roleName = "E", secondParam = "E"),
  F(roleName = "F", secondParam = "F");

  companion object {
    fun fromRoleName(roleName: String): RoleB? =
        values().singleOrNull { it.roleName == roleName }
  }
}

As you can see, A is part of both enums, but I would ideally want them to be the same object. Likewise I want to have the ability to create more of these enums in the future in case I need more types of roles.

Before I tried sealed interfaces, I simply had 1 big enum called UserRole that simply had all the values, and I used another class called RoleType and a simple mapping between the two to get what I wanted, but I don't think it does exactly what I want. Can anyone suggest a better way to categorize enum values?


Solution

  • You could reflect your roles in the type system by defining a sealed interface for each role type like this:

    sealed interface Type1 {
        val t: Int
        fun test()
    }
    
    sealed interface Type2 {
        val s: String
    }
    

    Then your Role class could be defined as a sealed class and every class could implement your Role types as fit.

    sealed class Role
    
    class A(override val t: Int, override val s: String) : Role(), Type1, Type2 {
        override fun test() {
            print("hello")
        }
    }
    
    class B(override val s: String) : Role(), Type2
    

    This does bring some overhead in the amount of code necessary to define each Role, so be aware of this when weighing pros and cons of each variant.