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

Access a value's parent naming from within the instantiated class (Scala)?

假定 Scala 2.11。我正在编写一个 class 来保留 Scala 值。它的目的是这样使用:

class ParentClass {

  val instanceId: String = "aUniqueId"

  val statefulString: Persisted[String] = persisted { "SomeState" }

  onEvent {
    case NewState(state) => statefulString.update(state)
  }

}

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

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

感谢您的帮助!

编辑

这个问题可能会有帮助:How can I get the memory location of a object in 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"))
  }

}