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 */ }
}
}
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)
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 _ =>
}
}
}
精彩评论