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

发布于 2024-10-14 20:36:28 字数 1237 浏览 2 评论 0原文

定义以下代码:

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

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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

寂寞笑我太脆弱 2024-10-21 20:36:28

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 an Array[Object] at the JVM level.

For any other class, type parameters are erased to Object, so List[Int] and List[Object] have the same representation at the JVM level.

彼岸花似海 2024-10-21 20:36:28

方法toArray 创建具有T 类型元素的新数组。并且根据文档

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

Method toArray creates new array with elements of type T. And according to documentation:

So depending on the actual type parameter for T, this could be an Array[Int], or an Array[Boolean], or an array of some of the other primitive types in Java, or an array of some reference type. But these types have all different runtime representations, so how is the Scala runtime going to pick the correct one? In fact, it can't do that based on the information it is given, because the actual type that corresponds to the type parameter T is erased at runtime.

简短的答案是因为这就是方法在 API 中定义的方式< /a>:

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

如果您在代码中的 def test[T:ClassManifest] 中省略了 :ClassManifest,那么编译器只知道它具有某种未知类型 < code>T,因此编译器无法找到该类型的 ClassManifest

为什么代码需要 ClassManifest?如果您 查看 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 才能创建新数组。

The short answer is because that's how the methods are defined in the API:

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

If you leave off the :ClassManifest in def test[T:ClassManifest] in your code, then all the compiler knows is that it has some unknown type T and therefore the compiler has no way of finding a ClassManifest for that type.

Why does the code need a ClassManifest? If you look at the source for toArray you'll see:

val result = new Array[B](size)

This constructor of Array requires a ClassManifest. See Easy Angel's answer for the documentation of this. Here's an example demonstrating it in the 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)

So basically, you have to write T: ClassManifest because Scala needs a ClassManifest in order to create a new array.

用心笑 2024-10-21 20:36:28

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 of T 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).

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文