什么是 Scala 中的 Manifest? 什么时候需要它?

自从 Scala 2.7.2以来,有一种叫做 Manifest的东西,它是 Java 类型擦除的一种变通方法。但是 Manifest到底是如何工作的,为什么/什么时候需要使用它?

Jorge Ortiz 的博客文章 清单: 实化类型解释了其中的一些内容,但没有解释如何将其与 上下文界限一起使用。

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

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

36574 次浏览

不是一个完整的答案,但关于 ManifestClassManifest之间的差异,你可以在 Scala 2.8 Array论文中找到一个例子:

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

唯一的方法是需要额外的运行时信息来描述类型 T。Scala 2.8为此提供了一种新的机制,称为 旅客名单Manifest[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上使用上下文界限1,

(见此 那么问题来了)

,给予:

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 编译器可以创建一个类清单作为隐式参数传递给表格。

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

在不知道更多细节的情况下,清单是否有利于您所看到的错误还不清楚。

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")
}

Manifest 用于具体化泛型类型,这些类型被类型擦除以在 JVM 上运行(JVM 不支持泛型)。然而,他们有一些严重的问题: 他们过于简单化,并且不能完全支持 Scala 的类型系统。因此,它们在 Scala 2.10中是 不赞成,并被取代为 TypeTag(本质上就是 Scala 编译器本身用来表示类型的东西,因此完全支持 Scala 类型)。有关差异的更多细节,请参见:

换句话说

你什么时候需要?

2013-01-04之前,当 Scala 2.10发布的时候

让我们再看看 scala源(Manifest.scala)中的 manifest,我们看到:

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"
}
}

我们可以看到 manifest function搜索一个隐式的 m: Manifest[T],它满足您在我们的示例代码中提供的 type parameter,它是 manifest[String]。所以当你调用这样的东西:

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

你正在检查当前定义在函数中的 implicit m是否为 manifest[String]类型,由于 manifestmanifest[T]类型的函数,它将搜索一个特定的 manifest[String],并且会发现是否存在这样一个隐式的。