为什么 Array 需要 ClassManifest 而 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 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
?
Define the following code:
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)
Here, I need to use ClassManifest
for it to compile correctly, otherwise I get the error:
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
^
On the other hand, the alternate code below using List
does not require this and compiles fine.
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
Note that the final output of testFunc
and testFunc1
are identical.
How come the List
version does not require a ClassManifest
?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
Java 中的数组不会进行类型擦除,特别是,
Array[Int]
与 JVM 级别的Array[Object]
不同。对于任何其他类,类型参数都会被删除为
Object
,因此List[Int]
和List[Object]
在 JVM 中具有相同的表示形式等级。Arrays in Java are not type-erased, and, in particular, an
Array[Int]
is different than anArray[Object]
at the JVM level.For any other class, type parameters are erased to
Object
, soList[Int]
andList[Object]
have the same representation at the JVM level.方法
toArray
创建具有T
类型元素的新数组。并且根据文档:Method
toArray
creates new array with elements of typeT
. And according to documentation:简短的答案是因为这就是方法在 API 中定义的方式< /a>:
如果您在代码中的
def test[T:ClassManifest]
中省略了:ClassManifest
,那么编译器只知道它具有某种未知类型 < code>T,因此编译器无法找到该类型的ClassManifest
。为什么代码需要
ClassManifest
?如果您 查看 toArray 的源代码,您会发现:Array
的这个构造函数需要一个ClassManifest
。有关此文档,请参阅 Easy Angel 的答案。下面是一个在 REPL 中演示它的示例:所以基本上,您必须编写
T: ClassManifest
因为 Scala 需要ClassManifest
才能创建新数组。The short answer is because that's how the methods are defined in the API:
If you leave off the
:ClassManifest
indef test[T:ClassManifest]
in your code, then all the compiler knows is that it has some unknown typeT
and therefore the compiler has no way of finding aClassManifest
for that type.Why does the code need a
ClassManifest
? If you look at the source for toArray you'll see:This constructor of
Array
requires aClassManifest
. See Easy Angel's answer for the documentation of this. Here's an example demonstrating it in the REPL:So basically, you have to write
T: ClassManifest
because Scala needs aClassManifest
in order to create a new array.Scala 使用 JVM 本机数组作为
Array
的实现。此类只能用已知类型创建。由于 Sun 决定创建遗留工件,通用类型被删除并且不再出现在字节代码中。这意味着在运行时,
Array[T]
不再知道T
的值,因此虚拟机无法创建该数组。当您尝试兼容 Java 1.4 并在数组上引入泛型时,会遇到一些更复杂的陷阱(我也没有了解全部深度),但底线是:JVM/Java 的数组不是泛型的; Scala 帮助我们进行通用抽象。因此,您提到的类清单本质上是一种黑客行为。当通过要求此隐式参数创建数组时,它静态地确保类型信息可用。实际上,任何创建类型参数数组的方法/函数都必须需要隐式清单,并且其所有被调用者等直到类型已知(静态)为止。
Scala uses JVM native arrays as implementation for
Array
. Such can only be created with known type.Since Sun decided to create legacy artifacts, generic types are erased and no longer present in byte code. That means that at runtime,
Array[T]
does no longer know the value ofT
and therefore, the VM can not create the array. There are some more complicated pitfalls you get when you try being compatible to Java 1.4 and introducing generics on array (I don't get the whole depth either), but bottom line is: JVM/Java's arrays are not generic; Scala helps us with the generic abstraction.The class manifest you mention is, consequently, essentially a hack. It ensures statically that type information is available when an array is to be created by demanding this implicit parameter. In practice, any method/function that creates an array of a type parameter has to demand an implicit manifest, and all its callees and so on up to the point were the type is known (statically).