1

Scala fiddle here

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

How do I access and initialise object members(name and test) with reflection?

tenshi
  • 26,268
  • 8
  • 76
  • 90
flavian
  • 28,161
  • 11
  • 65
  • 105

1 Answers1

1

You can achieve this with scala reflection API:

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

You need to make EarlyInit a class in order to be able to get a TypeTag evidence. Then you just search for all modules and access them (they would be initialized in the process)


Update

You can also simplify EarlyInit a bit and get rid of type parameter. You actually can get a Type of this directly from mirror:

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)
}
tenshi
  • 26,268
  • 8
  • 76
  • 90
  • If you are using reflection API, you need to add `scala-reflect` dependency. In SBT, for example: `libraryDependencies += "org.scala-lang" % "scala-reflect" % scalaVersion.value` – tenshi Jan 26 '14 at 18:54
  • And don't be confused by the fact that you can access the reflection library from the console (REPL launched by SBT) even without that dependency specified! — It puzzled me at first. – Randall Schulz Jan 26 '14 at 19:03
  • @flavian I checked this particular example, and it works fine... scala reflection is not thread-safe, so maybe you have several threads that causing this, but generally it's hard to say without seeing the actual code – tenshi Jan 26 '14 at 19:24
  • @flavian looks like repo mysteriously disappeared as I wanted to create a PR :) anyways, the problem is, that during initialization, `CassandraTable._columns` is null during the `EarlyInit` initialization, which would be initialized first. So you see `ExceptionInInitializerError` in this case, because you want to access not-yet-initialized properties of subclass. In order to solve this problem, you can make `_columns` lazy val: `lazy val _columns`. – tenshi Jan 26 '14 at 20:42
  • it's [OlegIlyenko](https://github.com/OlegIlyenko) :) but generally you can solve the problem by making private vals like `_columns` in `CassandraTable` lazy. – tenshi Jan 26 '14 at 20:56
  • Is there some way to make it work when SomeThing is a singleton object instead of a class? – Michael Jess Nov 10 '14 at 01:16
  • @MichaelJess I think the same technique can be applied. Even in this example the result is an object, so you just need to extend `EarlyInit` in your singleton object and it should work. – tenshi Nov 10 '14 at 18:56
  • @tenshi I figured so, but when I tried to eagerly initialize a singleton object with nested singleton objects Scala would complain about reflecting static modules. Maybe I'll open a new question w.r.t. that, but for now I found another solution to the problem I intended to solve. – Michael Jess Nov 11 '14 at 07:26