Scala: используя отражение, чтобы обнаружить свои внутренние объекты (и желания)? - PullRequest
5 голосов
/ 04 августа 2011

Есть ли способ во время выполнения обнаружить объекты, объявленные внутри внешнего объекта?Java Class методы getClasses и getDeclaredClasses оба возвращают пустые массивы.

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

Вывод:

Children of Parent:
  getClasses found 0
  getDeclaredClasses found 0

РЕДАКТИРОВАТЬ: У меня естьИсследовано, чтобы дети регистрировались у родителей:

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

(Хотя это выглядит ужасно, на самом деле все нормально, потому что я могу скрыть эти детали с помощью творческого подкласса и неявных параметров.)

Проблемапри таком подходе статические инициализаторы не вызываются до тех пор, пока не будет указана ссылка на каждый тип (отсюда вызовы Parent.Child1 и Parent.Child2), что противоречит цели.Вывод:

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

РЕДАКТИРОВАТЬ 2: Я знаю, что данные есть!Внутренние объекты перечислены с использованием 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 */ }
  }
}

Ответы [ 2 ]

1 голос
/ 01 декабря 2011

Только что заново применив его для своих собственных нужд (Riak standalone), Lift (найдите его в MetaRecord.scala) делает это так (упрощенно в моем случае):

  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 _ =>
      }
    }
  }
1 голос
/ 04 августа 2011

Почему бы не рассмотреть более простой подход, если можно зарегистрировать внутренние объекты:

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

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

ВТОРАЯ ПОПЫТКА:

Оборачивая дочерние объекты вНапример, их можно получить по рефлексии, не регистрируя их по отдельности.Однако есть некоторые накладные расходы:

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