Scala:对反射/运行时实例使用正确的类型类实例

时间:2017-10-04 09:35:22

标签: scala reflection typeclass

我有一个简单的特质

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的所有实现者的某种方式?

1 个答案:

答案 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,在这种情况下你需要一些后备。它也很慢。

相关问题