在提供内部 class 的实例时查找外部 class
Find the outer class when provided an instance of the inner class
有没有办法使用宏而不是 run-time reflection 从内部 class 的实例中获取父 class?
我有一组 class 这样的:
trait IdProvider {
type IdObject = Id.type
case class Id(underlying: Int)
}
case class SomeEntity(id: SomeEntity.Id)
object SomeEntity extends IdProvider
以及一些适用于任意 IdProvider#Id
s 的代码:
val lookup = Map[IdProvider#IdObject, Set[Operation]]
def can(operation: Operation, id: IdProvider#Id): Boolean = {
val idObject = findIdTypeFromInstance(id) // This is what I don't have
lookup.get(idObject).exists(s => s(operation))
}
借鉴 this gist by Paul P. 我现在有了这个宏:
def findIdTypeFromInstance[T <: AnyRef : c.WeakTypeTag](
c: blackbox.Context)(thing: c.Expr[T]): c.Expr[T] = {
import c.universe._
val companion = thing.actualType.typeSymbol.companion match {
case NoSymbol =>
c.abort(c.enclosingPosition, s"Instance of ${thing.actualType} has no companion object")
case sym => sym
}
def make[U: c.WeakTypeTag] = c.Expr[U](internal.gen.mkAttributedRef(companion))
make(c.WeakTypeTag(companion.typeSignature))
}
这适用于更简单的情况(顶级情况 classes、classes 和对象,甚至嵌套情况 classes)。但是,在处理上面的 IdProvider
设置时,宏会尝试生成此树:
Select(This(TypeName("IdProvider")), TermName("Id"))
这导致在我的测试中出现非常长的堆栈跟踪,其开头为:
scala.reflect.internal.Types$TypeError: value is not a member of my.spec.MacroSpec
我无法找到从实例或同伴 (IdProvider#Id
) 到父 class(在本例中为 SomeEntity
)的路径。有什么方法可以到达 SomeEntity
还是我必须使用 run-time reflection?
Id
companion 基本上是一个惰性 val。您需要封闭实例来检索其值,因为它不是静态定义的稳定路径。
使用 -Yshow-syms
你可以看到它被添加到 mixin
阶段:
object SomeEntity
constructor SomeEntity
* method Id$lzycompute (private)
method apply (case <synthetic>)
value id
method readResolve (private <synthetic>)
method unapply (case <synthetic>)
value x[=10=] (<synthetic>)
* object Id (<synthetic> <stable>)
value <local SomeEntity>
* variable Id$module (private <local> <synthetic>)
Id
的 $outer
字段添加到 explicitouter
。
显式公开伴随引用是否更容易?
case class Id(underlying: Int) {
def c = Id
}
这只是快速浏览;也许有一个聪明的方法。
有没有办法使用宏而不是 run-time reflection 从内部 class 的实例中获取父 class?
我有一组 class 这样的:
trait IdProvider {
type IdObject = Id.type
case class Id(underlying: Int)
}
case class SomeEntity(id: SomeEntity.Id)
object SomeEntity extends IdProvider
以及一些适用于任意 IdProvider#Id
s 的代码:
val lookup = Map[IdProvider#IdObject, Set[Operation]]
def can(operation: Operation, id: IdProvider#Id): Boolean = {
val idObject = findIdTypeFromInstance(id) // This is what I don't have
lookup.get(idObject).exists(s => s(operation))
}
借鉴 this gist by Paul P. 我现在有了这个宏:
def findIdTypeFromInstance[T <: AnyRef : c.WeakTypeTag](
c: blackbox.Context)(thing: c.Expr[T]): c.Expr[T] = {
import c.universe._
val companion = thing.actualType.typeSymbol.companion match {
case NoSymbol =>
c.abort(c.enclosingPosition, s"Instance of ${thing.actualType} has no companion object")
case sym => sym
}
def make[U: c.WeakTypeTag] = c.Expr[U](internal.gen.mkAttributedRef(companion))
make(c.WeakTypeTag(companion.typeSignature))
}
这适用于更简单的情况(顶级情况 classes、classes 和对象,甚至嵌套情况 classes)。但是,在处理上面的 IdProvider
设置时,宏会尝试生成此树:
Select(This(TypeName("IdProvider")), TermName("Id"))
这导致在我的测试中出现非常长的堆栈跟踪,其开头为:
scala.reflect.internal.Types$TypeError: value is not a member of my.spec.MacroSpec
我无法找到从实例或同伴 (IdProvider#Id
) 到父 class(在本例中为 SomeEntity
)的路径。有什么方法可以到达 SomeEntity
还是我必须使用 run-time reflection?
Id
companion 基本上是一个惰性 val。您需要封闭实例来检索其值,因为它不是静态定义的稳定路径。
使用 -Yshow-syms
你可以看到它被添加到 mixin
阶段:
object SomeEntity
constructor SomeEntity
* method Id$lzycompute (private)
method apply (case <synthetic>)
value id
method readResolve (private <synthetic>)
method unapply (case <synthetic>)
value x[=10=] (<synthetic>)
* object Id (<synthetic> <stable>)
value <local SomeEntity>
* variable Id$module (private <local> <synthetic>)
Id
的 $outer
字段添加到 explicitouter
。
显式公开伴随引用是否更容易?
case class Id(underlying: Int) {
def c = Id
}
这只是快速浏览;也许有一个聪明的方法。