2011-01-31 8 views
11

Definir el siguiente código:¿Por qué se necesita ClassManifest con Array pero no con 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) 

Aquí, necesito utilizar ClassManifest para que se compile correctamente, de lo contrario me sale el 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 
         ^

Por otra parte, el código alternativo a continuación utilizando List no requiere esto y compila bien.

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 

Tenga en cuenta que la salida final del testFunc y testFunc1 son idénticos.

¿Cómo es que la versión List no requiere un ClassManifest?

Respuesta

10

Las matrices en Java no se borran, y, en particular, un Array[Int] es diferente de en el nivel JVM.

Para cualquier otra clase, parámetros de tipo se borran a Object, por lo List[Int] y List[Object] tener la misma representación a nivel JVM.

+0

La respuesta de usted y de Angel juntas combinadas forman la respuesta correcta :) – Jus12

2

La respuesta corta es porque así es como los métodos son defined in the API:

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

Si deja fuera de la :ClassManifest en def test[T:ClassManifest] en su código, entonces todo el compilador sabe es que tiene algún tipo desconocido T y por lo tanto el compilador no tiene forma de encontrar un ClassManifest para ese tipo.

¿Por qué el código necesita un ClassManifest? Si look at the source for toArray verá:

val result = new Array[B](size) 

Este constructor de Array requiere un ClassManifest. Vea la respuesta de Easy Angel para la documentación de esto. He aquí un ejemplo que demuestra que en el 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) 

Así que, básicamente, tiene que escribir T: ClassManifest porque Scala necesita un ClassManifest con el fin de crear una nueva matriz.

10

Método toArray crea una nueva matriz con elementos del tipo T. Y 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.

2

Scala utiliza matrices nativas de JVM como la implementación de Array. Tal solo se puede crear con el tipo conocido.

Desde que Sun decidió crear artefactos heredados, los tipos genéricos se borran y ya no están presentes en el código de bytes. Eso significa que en tiempo de ejecución, Array[T] ya no sabe el valor de T y, por lo tanto, la VM no puede crear la matriz. Hay algunos inconvenientes más complicados que obtienes cuando intentas ser compatible con Java 1.4 e introducir genéricos en la matriz (tampoco obtengo toda la profundidad), pero la línea de fondo es: las matrices de JVM/Java no son genéricas; Scala nos ayuda con la abstracción genérica.

El manifiesto de clase que menciona es, en consecuencia, esencialmente un truco. Garantiza estáticamente que la información de tipo está disponible cuando se va a crear una matriz exigiendo este parámetro implícito. En la práctica, cualquier método/función que crea una matriz de un parámetro de tipo tiene que exigir un manifiesto implícito, y todas sus calles, y así sucesivamente, hasta el punto donde el tipo se conoce (estáticamente).

+0

"presumiblemente porque los objetos en realidad se ponen en el montón de forma secuencial" No, no lo son. Las matrices de objetos almacenan referencias secuencialmente, no objetos en sí mismos. (Esto no se aplica a las matrices de primitivos, por supuesto.) –

+1

Editado a favor de un razonamiento más vago. Atornille el legado, seriamente ... – Raphael

Cuestiones relacionadas