从实例化类 (Scala) 中访问值的父命名?



假设Scala 2.11。我正在编写一个持久化Scala值的类。它的目的是这样使用:

class ParentClass {
  val instanceId: String = "aUniqueId"
  val statefulString: Persisted[String] = persisted { "SomeState" }
  onEvent {
    case NewState(state) => statefulString.update(state)
  }
}

Persisted是一个具有类型参数的类,其目的是像缓存一样持久化特定的值,而Persist处理与持久化相关的所有逻辑。然而,为了简单地实现,我希望检索有关它的实例化的信息。例如,如果它在父类中的实例被命名为statefulString,我如何从Persisted类本身访问该名称?

这样做的目的是在简化API的同时防止自动命名持久值时发生冲突。我不能依赖于使用type,因为可能有多个String类型的值。

谢谢你的帮助!

编辑

这个问题可能是有帮助的:我怎么能得到一个对象的内存位置在java?

编辑2

阅读ScalaCache的源代码后,似乎有一种方法可以通过WeakTypeTag做到这一点。有人能解释一下它的宏里到底发生了什么吗?

https://github.com/cb372/scalacache/blob/960e6f7aef52239b85fa0a1815a855ab46356ad1/core/src/main/scala/scalacache/memoization/Macros.scala

在Scala宏和反射的帮助下,我能够做到这一点,并从ScalaCache中改编了一些代码:

class Macros(val c: blackbox.Context) {
  import c.universe._
  def persistImpl[A: c.WeakTypeTag, Repr: c.WeakTypeTag](f: c.Tree)(keyPrefix: c.Expr[ActorIdentifier], scalaCache: c.Expr[ScalaCache[Repr]], flags: c.Expr[Flags], ec: c.Expr[ExecutionContext], codec: c.Expr[Codec[A, Repr]]) = {
    commonMacroImpl(keyPrefix,  scalaCache, { keyName =>
      q"""_root_.persistence.sync.caching($keyName)($f)($scalaCache, $flags, $ec, $codec)"""
    })
  }
  private def commonMacroImpl[A: c.WeakTypeTag, Repr: c.WeakTypeTag](keyPrefix: c.Expr[ActorIdentifier], scalaCache: c.Expr[ScalaCache[Repr]], keyNameToCachingCall: (c.TermName) => c.Tree): Tree = {
    val enclosingMethodSymbol = getMethodSymbol()
    val valNameTree = getValName(enclosingMethodSymbol)
    val keyName = createKeyName()
    val scalacacheCall = keyNameToCachingCall(keyName)
    val tree = q"""
          val $keyName = _root_.persistence.KeyStringConverter.createKeyString($keyPrefix, $valNameTree)
          $scalacacheCall
        """
    tree
  }
  /**
    * Get the symbol of the method that encloses the macro,
    * or abort the compilation if we can't find one.
    */
  private def getValSymbol(): c.Symbol = {
    def getValSymbolRecursively(sym: Symbol): Symbol = {
      if (sym == null || sym == NoSymbol || sym.owner == sym)
        c.abort(
          c.enclosingPosition,
          "This persistence block does not appear to be inside a val. " +
            "Memoize blocks must be placed inside vals, so that a cache key can be generated."
        )
      else if (sym.isTerm)
        try {
          val termSym = sym.asInstanceOf[TermSymbol]
          if(termSym.isVal) termSym
          else getValSymbolRecursively(sym.owner)
        } catch {
          case NonFatal(e) => getValSymbolRecursively(sym.owner)
        }
      else
        getValSymbolRecursively(sym.owner)
    }
    getValSymbolRecursively(c.internal.enclosingOwner)
  }
  /**
    * Convert the given method symbol to a tree representing the method name.
    */
  private def getValName(methodSymbol: c.Symbol): c.Tree = {
    val methodName = methodSymbol.asMethod.name.toString
    // return a Tree
    q"$methodName"
  }
  private def createKeyName(): TermName = {
    // We must create a fresh name for any vals that we define, to ensure we don't clash with any user-defined terms.
    // See https://github.com/cb372/scalacache/issues/13
    // (Note that c.freshName("key") does not work as expected.
    // It causes quasiquotes to generate crazy code, resulting in a MatchError.)
    c.freshName(c.universe.TermName("key"))
  }
}

相关内容

  • 没有找到相关文章

最新更新