【问题标题】:How does one completely avoid runtime reflection in Scala?如何在 Scala 中完全避免运行时反射?
【发布时间】:2018-10-12 19:02:06
【问题描述】:

注意这是一个社区帖子,应根据需要添加示例。如果您无法直接编辑答案以添加示例(问题示例或解决方案),请在评论中发布带有指向要点(或类似内容)的链接或添加单独的答案,以便稍后集成。

Scala 3 可能根本不包含scala.reflect.runtime(Dotty 目前不包含,并且不确定是否计划这样做)。虽然适用于 Scala 2 和 Dotty 的答案可能更适合用于过渡目的和立即提高性能,但也欢迎特定于 Dotty 的解决方案。

参考文献

https://www.cakesolutions.net/teamblogs/ways-to-pattern-match-generic-types-in-scala

【问题讨论】:

  • 其实反之亦然。 TypeTags 完全在编译时生成,但可以将值传递给运行时。它们捕获有关泛型类型(以及更多)的信息,这些信息在运行时被 JVM 擦除。另一方面,虽然ClassTags 可以在编译时生成,但也可以在运行时生成它们。它们仅封装运行时可用的信息,因此它们中的泛型被删除,这就是您示例中的匹配失败的原因。
  • 来自Scala docClassTagsTypeTags 的一个较弱的特例,因为它们只包装给定类型的运行时类,而TypeTag 包含所有静态类型信息.也就是说,ClassTags 是由只知道一个类型的顶级类构成的,而不必知道它的所有参数类型。此运行时信息足以创建运行时数组。

标签: scala reflection scala-3 dotty


【解决方案1】:

一般建议

TypeTags 在编译时生成(由于每个使用站点的宏扩展,这可能会产生大量的编译时间开销)并在运行时使用,也会产生一些潜在的运行时开销,具体取决于它们的使用性质.因此,即使在 Scala 2 中,它们也可能只作为最后的手段使用(我们希望在这里解决所有此类问题,因此不需要最后的手段)。相比之下,直接或间接使用instanceOf 的东西都非常快。

使用 Java 反射

instanceOfsuper fastclassOf(即 Java 的 getClass)几乎一样快。

使用类标签

ClassTags 上的引用相等也应该非常快。

使用包装类型作为类型类的实例化

如果可能,您可能需要考虑将您的类型包装在一个类中,以赋予它一个具体的“Java”类型。虽然通常会有开销,但您可以使用value classes

包装类上的类型类通常是公开功能的好方法。顺便说一句,正如@Blaisorblade 指出的那样,“类型标记只是一个无法无天的类型类(使用方法typeName: Stringtpe: Type)+ 实例实现”。这将我们带到下一个选项:

根据需要使用宏

(目前 Dotty 不支持,但计划中)

虽然可能有点难以习惯,但最终结果应该是使用宏的代码中的语法比使用 TypeTag 时更清晰。此外,宏的用途远远超出TypeTag

精选示例

匹配集合的类型参数

示例

TypeTag 的一个典型用例(取自热门帖子 Scala: What is a TypeTag and how do I use it?)是对集合类型执行临时多态性:

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,所以它应该非常快。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-10-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-04-21
    • 2020-01-30
    相关资源
    最近更新 更多