Отражение Scala: принудительная инициация членов объекта
Скала скрипка здесь
import scala.reflect.ClassTag
import scala.language.existentials
class SomeClass[T <: SomeClass[T, R], R] extends EarlyInit[T, R] {}
trait EarlyInit[T <: SomeClass[T, R], R] {
self: SomeClass[T, R] =>
val clazz = getClass.getSuperclass
val potentialFields = clazz.getDeclaredClasses.toList
potentialFields.foreach {
field => {
// This correctly prints out fields of object members.
// but invokation throws an error every time.
println(s"${field.getName}")
}
}
}
class SomeThing extends SomeClass[SomeThing, Any] {
val stringTest = "test"
object name
object test
}
object SomeThing extends SomeThing {}
val x = SomeThing
Как получить доступ и инициализировать члены объекта (name
а также test
) с отражением?
1 ответ
Решение
Вы можете добиться этого с помощью API отражения Scala:
import scala.reflect.runtime.universe._
class SomeClass[T <: SomeClass[T, R]: TypeTag, R] extends EarlyInit[T] {}
class EarlyInit[T: TypeTag] {
val mirror = runtimeMirror(this.getClass.getClassLoader)
val reflection = mirror.reflect(this)
typeTag[T].tpe.members.filter(_.isModule).foreach(m => reflection.reflectModule(m.asModule).instance)
}
class SomeThing extends SomeClass[SomeThing, Any] {
val stringTest = "test"
object name {
println("name initialized")
}
object test {
println("test initialized")
}
}
object SomeThing extends SomeThing {}
val x = SomeThing
Вам нужно сделать EarlyInit
класс для того, чтобы иметь возможность получить TypeTag
доказательства. Затем вы просто ищете все модули и получаете к ним доступ (они будут инициализированы в процессе)
Обновить
Вы также можете упростить EarlyInit
немного и избавиться от параметра типа. Вы действительно можете получить Type
из this
прямо из зеркала:
trait EarlyInit {
val mirror = runtimeMirror(this.getClass.getClassLoader)
val reflection = mirror.reflect(this)
mirror
.classSymbol(this.getClass)
.toType
.members
.filter(_.isModule)
.foreach(m => reflection.reflectModule(m.asModule).instance)
}