2011-01-31 22 views
11

Definire il seguente codice:Perché ClassManifest è necessario con Array ma non 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) 

Qui, ho bisogno di usare ClassManifest per poter compilare correttamente, altrimenti ottengo l'errore:

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 
         ^

D'altra parte, il codice alternativo di seguito utilizzando List non richiede questo e compila bene.

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 

noti che l'output finale del testFunc e testFunc1 sono identici.

Come mai la versione List non richiede uno ClassManifest?

risposta

10

Gli array in Java non vengono cancellati dal testo e, in particolare, uno Array[Int] è diverso da uno Array[Object] a livello di JVM.

Per qualsiasi altra classe, i parametri di tipo vengono cancellati su Object, quindi List[Int] e List[Object] hanno la stessa rappresentazione a livello di JVM.

+0

La tua e la risposta di Angel combinate insieme formano la risposta corretta :) – Jus12

2

La risposta breve è perché è così che i metodi sono defined in the API:

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

Se si lascia fuori la :ClassManifest in def test[T:ClassManifest] nel codice, allora tutto il compilatore sa è che ha un certo tipo sconosciuto T e quindi il compilatore non ha modo di trovare un ClassManifest per quel tipo.

Perché il codice richiede uno ClassManifest? Se look at the source for toArray vedrete:

val result = new Array[B](size) 

Questo costruttore di Array richiede un ClassManifest. Vedi la risposta di Easy Angel per la documentazione di questo. Ecco un esempio che illustra nel 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) 

Quindi, fondamentalmente, si deve scrivere T: ClassManifest perché Scala ha bisogno di un ClassManifest al fine di creare un nuovo array.

10

Metodo toArray crea una nuova matrice con elementi di tipo T. E 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 usa JVM array native come implementazione per Array. Tale può essere creato solo con tipo conosciuto.

Poiché Sun ha deciso di creare artefatti legacy, i tipi generici vengono cancellati e non sono più presenti nel codice byte. Ciò significa che in fase di esecuzione, Array[T] non conosce più il valore di T e, pertanto, la VM non può creare la matrice. Ci sono alcune trappole più complicate che si ottengono quando si tenta di essere compatibili con Java 1.4 e l'introduzione di generici su array (non ho neanche l'intera profondità), ma la linea di fondo è: gli array JVM/Java non sono generici; Scala ci aiuta con l'astrazione generica.

Il manifest di classe che menzioni è, di conseguenza, essenzialmente un hack. Assicura staticamente che le informazioni sul tipo siano disponibili quando si deve creare una matrice richiedendo questo parametro implicito. In pratica, qualsiasi metodo/funzione che crea una matrice di un parametro di tipo deve richiedere un manifest implicito e tutti i suoi calle e così via fino al punto in cui il tipo è noto (staticamente).

+0

"presumibilmente perché gli oggetti vengono effettivamente messi nell'heap in sequenza" No, non lo sono. Le matrici di oggetti memorizzano i riferimenti in modo sequenziale, non gli oggetti stessi. (Questo non si applica agli array di primitivi, ovviamente.) –

+1

Modificato a favore di un ragionamento più vago. Vite legacy, sul serio ... – Raphael