Home > database >  Kotlin Creating a generic function that accepts any enum that implements an interface
Kotlin Creating a generic function that accepts any enum that implements an interface

Time:10-21

Currently I have multiple enums each with a userFriendly : string, e.g.

enum class TestGroup(val userFriendly: String) {
    A("A"),
    B("B")
}

For each enum I now have a separate function transforming them to a class SelectField:

class SelectField(
    val value: String, /** The value which gets submitted e.g. a UUID or other identifier */
    val text: String /** The human-friendly label e.g. the name of an organisation */
)

fun transformTestGroups(testGroups: Array<TestGroup>): List<SelectField> =
        testGroups.map {
            SelectField(
                value = it.name,
                text = it.userFriendly
            )
        }

I found that enums could implement an interface:

interface UserFriendly {
    val userFriendly: String
}
enum class TestGroup(override val userFriendly: String) : UserFriendly {
    A("A"),
    B("B")
}

However, I don't know how to make a genericTransform(values:Array<Enum<UserFriendly>>):List<SelectField>. Is this possible in Kotlin?

CodePudding user response:

Arrays in Kotlin are mutable and hence invariant. That's a fancy way of saying that an Array<Int> is not an Array<Any>. The reason for that is: If we could cast x: Array<Int> up to Array<Any>, then we could write x[0] = "ABC", since "ABC" is a valid Any, and now x would contain a value that is not an integer.

So simply using the supertype won't work here. But generics will. Specifically, we'll use the curiously recurring template pattern to require that our generic argument be an enum and a UserFriendly.

fun<T> transformTestGroups(testGroups: Array<T>): List<SelectField>
  where T: UserFriendly,
        T: Enum<T> =
    // Same implementation as before :)
    testGroups.map {
      SelectField(
        value = it.name,
        text = it.userFriendly
      )
    }

Complete runnable example:

interface UserFriendly {
  val userFriendly: String
}

enum class TestGroup(override val userFriendly: String): UserFriendly {
  A("A"),
  B("B")
}

data class SelectField(
  val value: String,
  val text: String
)

fun<T> transformTestGroups(testGroups: Array<T>): List<SelectField>
  where T: UserFriendly,
        T: Enum<T> =
    testGroups.map {
      SelectField(
        value = it.name,
        text = it.userFriendly
      )
    }

fun main(args: Array<String>) {
  println(transformTestGroups(arrayOf(TestGroup.A, TestGroup.B)))
}

Try it online!

  • Related