【问题标题】:What is a Manifest in Scala and when do you need it?什么是 Scala 中的 Manifest,什么时候需要它?
【发布时间】:2010-07-09 14:21:02
【问题描述】:

从 Scala 2.7.2 开始,有一个名为 Manifest 的东西,它是 Java 类型擦除的一种解决方法。但是Manifest 是如何工作的?为什么/什么时候需要使用它?

Jorge Ortiz 的博文 Manifests: Reified Types 解释了其中一些内容,但没有解释如何将其与 context bounds 一起使用。

还有,ClassManifest是什么,和Manifest有什么区别?

我有一些代码(较大程序的一部分,不能轻易将其包含在此处)有一些关于类型擦除的警告;我怀疑我可以通过使用清单来解决这些问题,但我不确定具体如何。

【问题讨论】:

标签: scala manifest


【解决方案1】:

编译器知道的关于类型的信息比 JVM 运行时可以轻松表示的更多。 Manifest 是编译器在运行时向代码发送有关丢失的类型信息的维度间消息的一种方式。

这类似于克莱普顿人在化石记录和人类“垃圾”DNA 中留下编码信息的方式。由于光速和引力共振场的限制,它们无法直接交流。但是,如果您知道如何收听他们的信号,您就可以通过难以想象的方式受益,例如决定午餐吃什么或玩哪个乐透号码。

尚不清楚 Manifest 是否有利于您在不了解更多细节的情况下看到的错误。

清单的一个常见用途是让您的代码根据集合的静态类型表现出不同的行为。例如,如果您想将 List[String] 与其他类型的 List 区别对待怎么办:

 def foo[T](x: List[T])(implicit m: Manifest[T]) = {
    if (m <:< manifest[String])
      println("Hey, this list is full of strings")
    else
      println("Non-stringy list")
  }

  foo(List("one", "two")) // Hey, this list is full of strings
  foo(List(1, 2)) // Non-stringy list
  foo(List("one", 2)) // Non-stringy list

对此的基于反射的解决方案可能涉及检查列表的每个元素。

上下文绑定似乎最适合在 scala 中使用类型类,Debasish Ghosh 对此进行了很好的解释: http://debasishg.blogspot.com/2010/06/scala-implicits-type-classes-here-i.html

上下文边界也可以使方法签名更具可读性。例如,上面的函数可以使用上下文边界重写,如下所示:

  def foo[T: Manifest](x: List[T]) = {
    if (manifest[T] <:< manifest[String])
      println("Hey, this list is full of strings")
    else
      println("Non-stringy list")
  }

【讨论】:

    【解决方案2】:

    Manifest 旨在具体化被类型擦除以在 JVM(不支持泛型)上运行的泛型类型。但是,它们有一些严重的问题:它们过于简单,并且无法完全支持 Scala 的类型系统。因此,它们在 Scala 2.10 中被弃用,并被 TypeTags(本质上是 Scala 编译器本身用来表示类型的东西,因此完全支持 Scala 类型)取代。有关差异的更多详细信息,请参阅:

    换句话说

    什么时候需要?

    2013-01-04 之前,when Scala 2.10 was released

    【讨论】:

    • 它还没有被弃用(但将会被弃用),因为 Scala 反射在 2.10 中仍处于试验阶段。
    • 2013-01-04 之前,或者如果您使用的 API 依赖于它。
    【解决方案3】:

    不是一个完整的答案,但是关于ManifestClassManifest的区别,你可以在Scala 2.8 Array paper找到一个例子:

    剩下的唯一问题是如何实现泛型数组创建。与 Java 不同,Scala 允许创建实例 new Array[T] 其中T 是一个类型参数。鉴于 Java 中不存在统一的数组表示,如何实现这一点?

    这样做的唯一方法是要求描述类型T 的附加运行时信息。 Scala 2.8 对此有一个新的机制,称为ManifestManifest[T] 类型的对象提供有关 T 类型的完整信息。
    Manifest 值通常以隐式参数传递;并且编译器知道如何为静态已知类型T 构造它们。

    还有一个名为 ClassManifest较弱形式,它可以通过只知道一个类型的顶级类来构造,而不必知道它的所有参数类型 .
    数组创建需要这种类型的运行时信息。

    例子:

    需要通过将ClassManifest[T] 传递到 方法作为隐式参数:

    def  tabulate[T](len:Int,  f:Int=>T)(implicit m:ClassManifest[T]) =  { 
      val  xs  =  new  Array[T](len) 
      for   (i  <- 0  until   len)  xs(i)   = f(i) 
      xs 
    } 
    

    作为一种简写形式,可以在类型参数T上使用上下文bound1,

    (见SO question for illustration

    ,给予:

    def  tabulate[T:    ClassManifest](len:Int,  f:Int=>T)  =  { 
      val  xs  =  new  Array[T](len) 
      for   (i  <- 0  until   len)  xs(i)   = f(i) 
      xs 
    } 
    

    当对 IntStringList[T] 等类型调用制表时,Scala 编译器可以创建一个类清单以作为隐式参数传递给制表。

    【讨论】:

      【解决方案4】:

      让我们也检查一下manifest 中的scala 来源 (Manifest.scala),我们看到:

      Manifest.scala:
      def manifest[T](implicit m: Manifest[T])           = m
      

      所以关于以下示例代码:

      def foo[A](somelist: List[A])(implicit m: Manifest[A]): String = {
        if (m <:< manifest[String]) {
          "its a string"
        } else {
          "its not a string"
        }
      }
      

      我们可以看到manifestfunction 搜索满足您在示例代码中提供的type parameter 的隐式m: Manifest[T],它是manifest[String]。所以当你打电话时:

      if (m <:< manifest[String]) {
      

      您正在检查您在函数中定义的当前implicit m 是否属于manifest[String] 类型,并且由于manifestmanifest[T] 类型的函数,它会搜索特定的manifest[String],它会查找是否有这样的隐含。

      【讨论】:

        猜你喜欢
        • 2011-04-05
        • 2011-01-28
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-08-20
        • 2012-09-16
        • 2010-10-15
        相关资源
        最近更新 更多