【问题标题】:Scala reflection: Force init of object membersScala反射:强制初始化对象成员
【发布时间】:2014-01-27 03:15:31
【问题描述】:

Scala 小提琴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

如何通过反射访问和初始化对象成员(nametest)?

【问题讨论】:

    标签: scala reflection


    【解决方案1】:

    您可以使用 Scala 反射 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
    

    您需要将EarlyInit 设为一个类才能获得TypeTag 证据。然后您只需搜索所有模块并访问它们(它们将在此过程中被初始化)


    更新

    您还可以稍微简化EarlyInit 并去掉类型参数。您实际上可以直接从镜像中获得thisType

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

    【讨论】:

    • 如果使用反射API,需要添加scala-reflect依赖。在 SBT 中,例如:libraryDependencies += "org.scala-lang" % "scala-reflect" % scalaVersion.value
    • 即使没有指定依赖项,您也可以从控制台(由 SBT 启动的 REPL)访问反射库这一事实不要感到困惑! — 一开始我很困惑。
    • @flavian 我检查了这个特定的示例,它工作正常...... scala 反射不是线程安全的,所以也许你有几个线程导致了这种情况,但通常很难说没有看到实际代码
    • @flavian 看起来 repo 神秘地消失了,因为我想创建一个 PR :) 无论如何,问题是,在初始化期间,CassandraTable._columnsEarlyInit 初始化期间为空,这将被初始化第一的。所以你在这种情况下看到ExceptionInInitializerError,因为你想访问子类的尚未初始化的属性。为了解决这个问题,可以使_columnslazy val:lazy val _columns
    • 它是 OlegIlyenko :) 但通常你可以通过在 CassandraTable 中设置像 _columns 这样的私有 val 来解决问题。
    猜你喜欢
    • 2014-04-19
    • 2017-01-17
    • 1970-01-01
    • 2018-02-15
    • 2011-09-19
    • 1970-01-01
    • 1970-01-01
    • 2020-02-22
    • 1970-01-01
    相关资源
    最近更新 更多