2010-07-09 79 views
116

由于Scala 2.7.2有一个名为Manifest的东西,它是Java类型擦除的解决方法。但Manifest如何正确工作以及为什么/何时需要使用它?什么是Scala中的清单,什么时候需要它?

博客文章Manifests: Reified Types由豪尔赫·奥尔蒂斯介绍了一些,但它并没有解释如何与context bounds一起使用。

另外,什么是ClassManifest,与Manifest有什么区别?

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

+2

关于Manifest/ClassManifest差异的讨论已在邮件列表中进行了讨论,请参阅http://scala-programming-language.1934581.n4.nabble.com/What-s-the-difference-between-ClassManifest-和 - Manifest-td2125122.html – 2010-07-09 23:23:57

+0

另请参见:[Scala:什么是TypeTag,我该如何使用它?](http://stackoverflow.com/questions/12218641/scala-what-is-a-typetag-and如何使用它) – Jesper 2015-10-09 10:31:49

回答

174

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

这与Kleptonians如何在化石记录中留下编码信息和人类的“垃圾”DNA相似。由于光速和重力场的限制,它们无法直接通信。但是,如果你知道如何调谐他们的信号,你可以以你无法想象的方式获益,从决定午餐吃什么或吃哪个乐透号码。

如果一个清单会在不了解更多细节的情况下有利于您看到的错误,那么目前尚不清楚。

Manifests的一个常见用法是让您的代码根据集合的静态类型行为不同。例如,如果你想从其他类型的列表的区别对待列表[字符串]什么:

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 

基于反射的解决方案,这可能会涉及到检查列表中的每个元素。

结合上下文,似乎最适合于使用阶型类,并深受Debasish戈什这里解释: 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") 
    } 
+13

Upvoted用例。希望我对外部Kleptonian部分进行第二次投票。 – huynhjl 2010-07-09 15:13:35

+8

啊哈,这是来自Scala编译器的一个秘密的内部消息... ;-) – Jesper 2010-07-10 06:05:06

+1

氪星人?无论如何,伟大的隐喻! – kirhgoff 2013-11-15 09:52:26

25

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

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

要做到这一点的唯一方法是需要额外的运行时信息,其中描述了类型T。斯卡拉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 
} 

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

(见本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 
} 

当调用的类型制表如Int,或String,或List[T],Scala编译器可以创建一个类清单作为隐式参数传递给列表。

22

一个清单是为了具体化泛型类型获取类型擦除在JVM上运行(不支持泛型)。然而,他们有一些严重的问题:它们太简单了,并且无法完全支持Scala的类型系统。他们因此在Scala 2中弃用。10,并替换为TypeTag(这实质上是Scala编译器本身用来表示类型的因素,因此完全支持Scala类型)。有关差异的详细信息,请参阅:

换句话说

当你需要它?

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

+0

它尚未被弃用(但将是),因为斯卡拉反射在2.10仍然是实验性的。 – Keros 2013-04-22 10:23:06

+0

在2013-01-04之前,或者如果您使用依赖它的API。 – 2013-12-19 23:16:26

1

我们也scala来源CHCK出manifestManifest.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搜索隐含m: Manifest[T],它满足您在我们的示例代码中提供的type parameter这是manifest[String]。所以,当你调用是这样的:

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

要检查,如果你在你的函数定义的当前implicit mmanifest[String]类型,作为manifest的是它会搜索特定manifest[String],它manifest[T]类型的函数会发现是否有这样一种隐含的。

相关问题