一般建议
TypeTag
s 在编译时生成(由于每个使用站点的宏扩展,这可能具有显着的编译时间开销)并在运行时使用,也会产生一些潜在的运行时开销,具体取决于它们使用的确切性质。因此,即使在 Scala 2 中,它们也可能只能作为最后的手段使用(我们希望在这里解决所有此类问题,因此不需要最后的手段)。相比之下,instanceOf
直接或间接使用 的东西非常快。
使用 Java 反射
instanceOf
超级快。classOf
(即 Java 的getClass
)几乎一样快。
使用 ClassTag
s上的引用相等ClassTag
也应该非常快。
使用包装类型作为类型类的实例化
如果可能,您可能需要考虑将您的类型包装在一个类中,给它一个具体的“Java”类型。虽然通常会有开销,但您可以使用值类。
包装类上的类型类通常是公开功能的好方法。顺便说一句,正如@Blaisorblade 指出的那样,“类型标记只是一个无法无天的类型类(带有方法typeName: String
和tpe: Type
)+实例实现”。这将我们带到了下一个选项:
如果需要,使用宏
(目前 Dotty 不支持,但计划中)
虽然可能有点难以习惯,但最终结果应该是使用宏的代码中的语法比使用TypeTag
. 此外,宏的用途远远超出TypeTag
.
精选示例
匹配集合的类型参数
例子
的一个典型用例TypeTag
,取自流行的帖子Scala:什么是 TypeTag 以及如何使用它?是对集合类型执行临时多态性:
import scala.reflect.runtime.universe._
def meth[A : TypeTag](xs: List[A]) = typeOf[A] match {
case t if t =:= typeOf[String] => "list of strings"
case t if t <:< typeOf[Foo] => "list of foos"
}
scala> meth(List("string"))
res67: String = list of strings
scala> meth(List(new Bar))
res68: String = list of foos
与ClassTag
,不同的TypeTag
是运行时反射。也许我在这里用错了,尽管这种行为非常令人惊讶。至少在 REPL 中,我没有收到以下任何警告:
def meth[A : ClassTag](xs: List[A]) = xs match {
case xs: List[String] => "list of strings"
case xs: List[Foo] => "list of foos"
}
meth(List(new Bar))
res10: String = "list of strings"
解决方案
这是来自 gitter 上的@smarter(假设我们不需要单独处理不同类型的空列表):
def meth[A](xs: List[A]) = xs match {
case Nil => "nil"
case (_: String) :: _ => "list of strings"
case (_: Foo) :: _ => 'list of foos"
}
这使用instanceOf
,所以它应该非常快。