2

I would like to know whether there is any way to create an enumeration class from a set (e.g., a set of strings).

Below is a simple attempt:

val s = Set("v1", "v2", "v3")

object sEnum extends Enumeration {
  for (v <- s) {
    val v = Value
  }
}

for (v <- sEnum.values) {
  println(v)
}

The enumeration object seems to compile, but it's values are wonky: the print loop shows that the values inside sEnum look like this:

<Invalid enum: no field for #0>

Thus, the problem seems to be at the value declaration

val v = Value

Is there some way that I can replace the variable v by its contents at run time (using reflection I assume)?

pnips
  • 153
  • 1
  • 7

1 Answers1

0

TL;DR: Look at the example at the bottom.

The values in your enum don't have string names (although they have unique numeric IDs). There are two ways how an enum value can get a string name:

  1. You pass it explicitly when calling Value, e.g. Value("v1")
  2. If you create a val field in the class, Scala infers the value from the field name, e.g. in val v1 = Value. This is done internally via reflection, by examining all 0-args methods which return a conforming type.

The second case doesn't apply to your example: val v = Value doesn't create a method on the class, v is just a local variable inside the loop (hiding the iteration variable v). So you need to take the first approach:

val s = Set("v1", "v2", "v3")
object MyEnum extends Enumeration {
  s.foreach(Value(_))
}
MyEnum.values.foreach(println) // prints v1 v2 v3

Now, this doesn't allow you to access your enum values easily, MyEnum.v1 won't work. There is no way how to add methods/fields dynamically to a class based on a set of string values. At least not without some magic bytecode manipulation where you probably don't want to venture.

Instead, you can define a custom method for retrieving the values. Here's an example using Symbols, so that you don't need to use the inefficient MyEnum.withName() method:

val s = Set("v1", "v2", "v3")

object MyEnum extends Enumeration {
  type MyEnum = Value
  s.foreach(Value(_))

  private val constants = s.map(v => Symbol(v) -> withName(v)).toMap
  def apply(c: Symbol): MyEnum = constants(c)
}

MyEnum.values.foreach(println) // prints v1 v2 v3
println(MyEnum('v1)) // prints v1
Community
  • 1
  • 1
Mifeet
  • 12,949
  • 5
  • 60
  • 108