The implementation is based on the Java reflection API.
If you define val for the enum values:
object Fruit extends Enumeration {
val Apple, Banana, Cherry = Value
}
There are methods for the val in the class Fruit:
scala> Fruit.getClass.getMethods filter (_.getName.contains("Apple")) foreach println
public scala.Enumeration$Value line10$object$$iw$$iw$Fruit$.Apple()
toString
calls Enumeration.this.nameOf(i)
if the name is not explicitly set. This method will try to find all methods in the enumeration class returning Value
instances.
val methods = getClass.getMethods
for (m <- methods
if (classOf[Value].isAssignableFrom(m.getReturnType) &&
!java.lang.reflect.Modifier.isFinal(m.getModifiers) &&
m.getParameterTypes.isEmpty &&
isValDef(m)))
These are the methods of the Fruit class.
It then takes the name of the methods and the ids of enum values to build a map id -> name
and retrieves the name from the map with the id of enum value.
val name = m.getName
// invoke method to obtain actual `Value` instance
val value = m.invoke(this)
// invoke `id` method
val idMeth = classOf[Val].getMethod("id")
val id: Int = idMeth.invoke(value).asInstanceOf[java.lang.Integer].intValue()
This implementation can be easily broken if you define a enum like this:
object X extends Enumeration {
val Y = Value
}
object Fruit extends Enumeration {
val x = X.Y
val A,B,C = Value
}
This Fruit.value returns object$Fruit.ValueSet(x, B, C)
not object$Fruit.ValueSet(A, B, C)
.