Using enums generically


#1

I'm developing a system that uses delegated properties to perform some operations on property values. For example, I have something like this for a string:

class StringField(var value : String) {
    fun get(thisRef: Any?, prop: PropertyMetadata): String {
        return value
    }

  fun set(thisRef: Any?, prop: PropertyMetadata, newValue: String) {
  // do something with the value
  value = newValue
  }
}


So that I can define my properties like:

var name by StringField(“default”)

I’d like to do the same thing with enums without having to define a new delegate for each enum type. I couldn’t figure out how to do it with actual enum types, but I came up with a slightly convoluted way with my own custom class:

open class StringEnum(var value : String = "")

open class StringEnumObject<T : StringEnum>() {
  val values = ArrayList<T>()

  inline fun <reified Tnew : T>value(s : String) : T {
  val inst = javaClass<Tnew>().newInstance()
  inst.value = s
  values.add(inst)
  return inst
  }
}


class StringEnumField<T : StringEnum>(var value : T) {
  fun get(thisRef: Any?, prop: PropertyMetadata): T {
  return value
  }

  fun set(thisRef: Any?, prop: PropertyMetadata, newValue: T) {
  // do something with the value
  value = newValue
  }
}


This way, the enums can be declared and used like this:

class Status(value : String = "") : StringEnum(value) {
    class object : StringEnumObject<Status>() {
        val open = value<Status>("open")
        val closed = value<Status>("closed")
    }
}

val status by StringEnumField(Status.open)

This is all fine and good, but it’s a bit verbose and I feel like I’m not using the type system properly. So the question: is there a way to make a generic property delegate that works on any enum?


#2

More specifically, I am able to make a generic field that uses enums, but am not able to instantiate new values. This is the core functionality I'm looking for (the abillity to persist these properties to/from JSON and a database).

class EnumField<T : Enum<T>>(var value : T) {
    fun get(thisRef: Any?, prop: PropertyMetadata): T {
        return value
    }

  fun set(thisRef: Any?, prop: PropertyMetadata, newValue: T) {
  value = newValue
  }
  
  fun fromString(raw : String) {
  value = // create a new value from raw
  }
}


So, I can’t figure out how to implement the function fromString().


#3

Why can't you take the class of `value` through reflection and call `Enum.valueOf(thatClass, stringValue)`? Or am I getting your use case wrong?


#4

Okay, you're right. I had tried this initially but it wasn't working because it seems like the Kotlin Enum class hides the java one? So I ended up needing to fully qualifiy it:

java.lang.Enum.valueOf(value.javaClass, s)

Thanks!