定义以下代码:
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
请注意testFunc
和testFunc1
的最终输出相同。
为什么List
版本不需要ClassManifest
?
答案 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帮助我们进行通用抽象。
你提到的课程清单因此基本上是一个黑客。当要通过要求此隐式参数创建阵列时,它可以静态地确保类型信息可用。实际上,任何创建类型参数数组的方法/函数都必须要求一个隐式清单,并且所有它的被调用者等等都是已知的类型(静态)。