views:

135

answers:

2

I have an enum Fruit defined as:

object Fruit extends Enumeration {
  val Apple, Banana, Cherry = Value
}

Now printing values of this enum, on Scala 2.7.x gives:

scala> Fruit foreach println
line1$object$$iw$$iw$Fruit(0)
line1$object$$iw$$iw$Fruit(1)
line1$object$$iw$$iw$Fruit(2)

However the same operation on Scala 2.8 gives:

scala> Fruit foreach println       
warning: there were deprecation warnings; re-run with -deprecation for details
Apple
Banana
Cherry

My question is:

How is the method toString in Enumeration in Scala 2.8 implemented? I tried looking into the source of Enumeration but couldn't understand anything.

A: 

In Scala 2.8.0.RC2 you can find the implementation of toString in the inner class Val in line 273 of Enumeration.scala. The instances of Val are instantiated through the Value method in line 192. Currently I don't understand how the names (Apple, Banana, ...) are extracted. May somebody can help out?

michael.kebe
+4  A: 

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).

Thomas Jung