我有一个简单的特质
trait BusinessObject {}
和一个简单的类型
trait Printer[T<:BusinessObject] { def print(instance:T): Unit }
在我的代码库中,我有几百个BusinessObject
的实现。一些是直接实现者,一些实现BusinessObject
的子特征,一些使用with
添加各种mixin特征。我有大约10种不同的Printer
特殊实现(在各种子特性和mixins上定义),以及任何其他BusinessObject
的低优先级通用回退实例,它有效。
我需要在代码库中记录BusinessObject
的所有实现,所以我使用Scala的反射机制来枚举这些,现在想要在每个上应用一个打印机。反射机制的方法签名是
def enumerateBOs: Traversable[BusinessObject]
它返回每个BusinessObject
实现的一个实例。我的问题是,在运行时似乎无法为此遍历中的每个对象获取正确的(特定)打印机。
我尝试使用.type
这样召唤:
enumerateBOs.head match { case bo => Printer[bo.type].print(bo) }
但我得到了每个元素的通用后备Printer
。
有什么办法可以做我想做的事吗?或者,如果implicits真的只在编译时可用,那么在编译时列出BusinessObject
的所有实现者的某种方式?
答案 0 :(得分:1)
Implicits(因为Scala中的所有泛型)实际上都是编译时机制,并且无法在编译时找到非密封特征的所有实现。
话虽如此,在运行时运行Scala编译器并不难。
获取您的依赖项:
libraryDependencies ++= Seq(
"org.scala-lang" % "scala-reflect" % "2.12.3",
"org.scala-lang" % "scala-compiler" % "2.12.3"
)
你只需要一个ToolBox
对象来完成所有事情 - 它解析一个字符串,然后将解析后的树编译成一个函数() => Any
,当调用它时,它会给出一个表达式的结果。该代码也无法访问周围的上下文,因此所有类型都必须完全限定或导入。
import scala.reflect.runtime._
import scala.tools.reflect.ToolBox
import scala.util.Try
def unsafeCompile[A](code: String): A = {
val tb = currentMirror.mkToolBox()
tb.compile(tb.parse(code))().asInstanceOf[A]
}
上述函数会抛出异常并且不会真正检查对A
的强制转换是否有效,因此如果使用不正确,您可以在未知位置获取ClassCastException
。
但是现在,在运行时获取实例只是一些LOC的问题:
enumerateBOs.map { obj =>
Try {
val f = unsafeCompile[Any => Unit](s"""
import your.package_.with_.Printer
// any additional imports for instances go there too
implicitly[Printer[_root_.${obj.getClass.getCanonicalName}]].print _
""")
f(obj)
}
}
我假设你没有使用匿名课程 - 他们的getCanonicalName
会返回null
,在这种情况下你需要一些后备。它也很慢。