为什么ClassManifest需要Array而不是List?

时间:2011-01-31 12:41:12

标签: scala scala-2.8 scala-collections

定义以下代码:

import scala.collection.JavaConversions._  
val iter:java.util.Iterator[Any] = Array[Any](1, 2, 3).iterator
def func(a:Any):String = a.toString

def test[T:ClassManifest](iter:java.util.Iterator[Any], func:Any=>T):Array[T] =  
  iter.map(i=>func(i)).toArray

def testFunc = test(iter, func)

在这里,我需要使用ClassManifest来正确编译,否则我会收到错误:

scala> def test[T](iter:java.util.Iterator[Any], func:Any=>T):Array[T] = 
     |   iter.map(i=>func(i)).toArray         

<console>:11: error: could not find implicit value for evidence parameter of 
type ClassManifest[T]
     iter.map(i=>func(i)).toArray
                          ^

另一方面,下面使用List的替代代码不需要这样,编译得很好。

import scala.collection.JavaConversions._  
val iter:java.util.Iterator[Any] = Array[Any](1, 2, 3).iterator
def func(a:Any):String = a.toString 

def test1[T](iter:java.util.Iterator[Any], func:Any=>T):List[T] = 
  iter.map(i=>func(i)).toList   


def testFunc1 = test1(iter, func).toArray

请注意testFunctestFunc1的最终输出相同。

为什么List版本不需要ClassManifest

4 个答案:

答案 0 :(得分:11)

Java中的数组不是类型擦除的,特别是Array[Int]与JVM级别的Array[Object]不同。

对于任何其他类,类型参数将被删除为Object,因此List[Int]List[Object]在JVM级别具有相同的表示。

答案 1 :(得分:10)

方法toArray创建包含T类型元素的新数组。 according to documentation

  

因此,根据T的实际类型参数,这可能是Array [Int],Array [Boolean],或Java中某些其他基本类型的数组,或某些引用类型的数组。但是这些类型具有所有不同的运行时表示,那么Scala运行时如何选择正确的运行时表示?实际上,它不能基于给出的信息来做到这一点,因为与类型参数T对应的实际类型在运行时被擦除。

答案 2 :(得分:2)

简短的回答是因为这些方法是defined in the API

def toArray [B >: A] (implicit arg0: ClassManifest[B]) : Array[B]
def toList : List[A]

如果你在代码中的:ClassManifest中没有def test[T:ClassManifest],那么所有编译器都知道它有一些未知的类型T,因此编译器无法找到该类型ClassManifest

为什么代码需要ClassManifest?如果您look at the source for toArray,您会看到:

val result = new Array[B](size)

Array的此构造函数需要ClassManifest。有关此文档的说明,请参阅Easy Angel的答案。这是一个在REPL中展示它的例子:

scala> def createArray[T] = new Array[T](10)
<console>:5: error: cannot find class manifest for element type T
       def createArray[T] = new Array[T](10)

基本上,您必须编写T: ClassManifest,因为Scala需要ClassManifest才能创建新数组。

答案 3 :(得分:2)

Scala使用JVM本机数组作为Array的实现。这样只能用已知类型创建。

由于Sun决定创建遗留工件,因此泛型类型将被删除,并且不再存在于字节代码中。这意味着在运行时,Array[T]不再知道T的值,因此VM无法创建数组。当你尝试兼容Java 1.4并在数组中引入泛型时,你会遇到一些更复杂的陷阱(我也没有得到整个深度),但最重要的是:JVM / Java的数组不是通用的; Scala帮助我们进行通用抽象。

你提到的课程清单因此基本上是一个黑客。当要通过要求此隐式参数创建阵列时,它可以静态地确保类型信息可用。实际上,任何创建类型参数数组的方法/函数都必须要求一个隐式清单,并且所有它的被调用者等等都是已知的类型(静态)。