Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Scala: using reflection to discover your inner objects (and desires)?

Is there a way to at runtime discover objects declared inside an outer object? The Java Class methods getClasses and getDeclaredClasses both return empty arrays.

object Parent {
    object Child1
    object Child2
}

println("Children of Parent:")
println("   getClasses found %d".format(Parent.getClass.getClasses.size))
println("   getDeclaredClasses found %d".format(Parent.getClass.getDeclaredClasses.size))

The output is:

Children of Parent:
  getClasses found 0
  getDeclaredClasses found 0

EDIT: I have explored having the children register themselves with the parent:

object Parent {
    val children = new collection.mutable.ListBuffer[AnyRef]
    object Child1 { Parent.children += this }
    object Child2 { Parent.children += this }
}

println("(1) Parent.children size: %d".format(Parent.children.size))
Parent.Child1
Parent.Child2
println("(2) Parent.children size: %d".format(Parent.children.size))

(Although this looks ugly, it's actually OK because I can hide these details with creative subclassing and implicit parameters.)

The problem with this approach is the static initializers are not called until each type is referenced (hence the calls to Parent.Child1 and Parent.Child2), which defeats the purpose. The output is:

(1) Parent.children size: 0
(2) Parent.children size: 2

EDIT 2: I know the data is there! The inner objects are listed using scalap Parent:

object Parent extends java.lang.Object with scala.ScalaObject {
  def this() = { /* compiled code */ }
  object Child1 extends java.lang.Object with scala.ScalaObject {
    def this() = { /* compiled code */ }
  }
  object Child2 extends java.lang.Object with scala.ScalaObject {
    def this() = { /* compiled code */ }
  }
}
like image 920
Landon Kuhn Avatar asked Nov 04 '22 17:11

Landon Kuhn


2 Answers

Why not consider a simpler approach, if it's ok to register the inner objects:

object Parent {
  object Child1 
  object Child2 
  val children = List( Child1, Child2 )
}

scala> Parent.children
res: List[ScalaObject] = List(Parent$Child1$@7493931b, Parent$Child2$@49f0d68)

SECOND TRY:

By wrapping the child objects in an instance, it is possible to retrieve them by reflection, without having to register them individually. However, there's some overhead:

trait HasChildren {
  val children: AnyRef
}

object Parent extends HasChildren {
  val children = new {
    object Child1
    object Child2
  }
}

scala> Parent.children.getClass.getDeclaredFields
res: Array[java.lang.reflect.Field] = 
  Array(private volatile Parent$$anon$1$Child1$ Parent$$anon$1.Child1$module,
  private volatile Parent$$anon$1$Child2$ Parent$$anon$1.Child2$module)
like image 122
paradigmatic Avatar answered Nov 09 '22 17:11

paradigmatic


Having just re-implemented it for my own needs (Riak standalone) Lift (find it in MetaRecord.scala) does it this way (simplified in my case):

  private def isField(m: Method) = classOf[RiakFieldWrapper[A, AnyRef]].isAssignableFrom(m.getReturnType)

  case class FieldHolder(name: String, method: Method, field: RiakFieldWrapper[A, AnyRef])

  def introspect(rec: BucketWrapper[A], methods: Array[Method]): Unit = {
    for (v <- methods if isField(v)) {
      v.invoke(rec) match {
    case rfw: RiakFieldWrapper[A, AnyRef] => tArray += FieldHolder(rfw.keyName, v, rfw)
    case _ =>
      }
    }
  }
like image 35
ariejdl Avatar answered Nov 09 '22 17:11

ariejdl