Отражение 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)
}
Другие вопросы по тегам