什么是Scala中的清单,什么时候需要它?
从Scala 2.7.2开始,有一种叫做Manifest
东西,它是Javatypes擦除的一个解决方法。 但是, Manifest
是如何正确工作的?为什么/何时需要使用它?
Jorge Ortiz的博客文章“Manifests:Reified Types”解释了其中的一些,但并没有解释如何将它与上下文边界一起使用。
另外,什么是ClassManifest
,与Manifest
什么不同?
我有一些代码(一个更大的程序的一部分,不能很容易地包含在这里),有一些关于types擦除的警告; 我怀疑我可以通过使用清单解决这些问题,但我不确定如何。
编译器知道更多关于types的信息,而不是JVM运行时可以轻易表示的信息 Manifest是编译器在运行时向代码发送间维信息的一种方式,用于告知丢失的types信息。
这与克里普顿人如何在化石logging中留下编码信息以及人类的“垃圾”DNA相似。 由于光速和引力场的限制,它们无法直接通信。 但是,如果你知道如何调谐他们的信号,你可以通过你无法想象的方式获益,从决定午餐吃什么或者吃哪个乐透号码。
如果一个清单能够在不知道更多细节的情况下获益于您所看到的错误,目前尚不清楚。
清单的一个常见用法是根据集合的静态types让您的代码行为不同。 例如,如果您想将列表[string]与其他types的列表进行不同的处理:
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
基于reflection的解决scheme可能涉及检查列表中的每个元素。
上下文绑定似乎最适合在scala中使用typestypes,在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
和ClassManifest
之间的区别,你可以在Scala 2.8 Array
find一个例子:
剩下的唯一问题是如何实现通用数组的创build。 与Java不同,Scala允许创build一个新的实例
Array[T]
,其中T
是一个types参数。 鉴于Java中不存在统一的数组表示,这怎么能够实现呢?唯一的方法是要求额外的描述types
T
运行时信息。 斯卡拉2.8有一个新的机制,这就是所谓的Manifest 。Manifest[T]
types的对象提供有关typesT
完整信息。
Manifest
值通常以隐式parameter passing; 编译器知道如何为静态已知typesT
构造它们。还有一个叫做
ClassManifest
的较弱forms ,它可以通过知道一个types的顶级类来构造,而不必知道它的所有参数types 。
正是这种types的运行时信息是创build数组所必需的。
例:
需要通过将
ClassManifest[T]
作为隐式parameter passing给方法来提供这些信息:
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 }
作为速记forms,可以在types参数
T
上使用上下文bound1,
(看到这个问题的插图 )
,给:
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 }
当在诸如
Int
,String
或List[T]
的types上调用tabulate时,Scala编译器可以创build一个类清单作为隐式parameter passing给Tabular。
一个Manifest旨在将genericstypes的types擦除在JVM上运行(不支持generics)。 但是,他们有一些严重的问题:太简单了,无法完全支持Scala的types系统。 因此,它们在Scala 2.10中被弃用 ,并被TypeTag
(它本质上是Scala编译器本身用来表示types,因此完全支持Scalatypes)所取代。 有关差异的更多详细信息,请参阅:
- Scala:什么是TypeTag,我如何使用它?
- 新的Scala TypeTags如何改进(不赞成使用的)清单?
换一种说法
你什么时候需要它?
在2013-01-04之前, Scala 2.10发布 。
我们还可以在scala
源代码中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" } }
我们可以看到manifest
function
search隐式的m: Manifest[T]
,它满足您在我们的示例代码中提供的type parameter
,它是manifest[String]
。 所以当你打电话的时候
if (m <:< manifest[String]) {
你正在检查你在你的函数中定义的当前implicit m
是否是typesmanifest[String]
并且由于manifest
是typesmanifest[T]
的函数,它将search特定的manifest[String]
,它会查找是否存在是这样一种隐含的。