2011-01-31 9 views
11

Zdefiniuj następujący kod:Dlaczego wymagany jest ClassManifest z tablicą, ale nie z 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) 

Tutaj muszę korzystać ClassManifest na to, aby skompilować poprawnie, w przeciwnym razie pojawia się błąd:

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 
         ^

Z drugiej strony, kod alternatywny poniżej korzystania List nie wymaga tego i kompiluje dobrze.

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 

Należy pamiętać, że ostateczna produkcja testFunc i testFunc1 są identyczne.

Dlaczego wersja List nie wymaga ClassManifest?

Odpowiedz

10

Tablice w języku Java nie są usuwane z tekstu, a w szczególności numer Array[Int] jest inny niż Array[Object] na poziomie maszyny JVM.

Dla każdej innej klasy parametry typu są usuwane do Object, więc List[Int] i mają tę samą reprezentację na poziomie JVM.

+0

Twoja odpowiedź z Anioła połączona razem tworzą poprawną odpowiedź :) – Jus12

2

Odpowiedź jest krótka, ponieważ jest to w jaki sposób metody są defined in the API:

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

Jeżeli pozostawisz wyłączyć :ClassManifest w def test[T:ClassManifest] w kodzie, a potem wszystko kompilator wie, że to ma jakiś nieznany typ T i dlatego kompilator nie ma możliwości znalezienia tego typu.

Dlaczego kod wymaga ClassManifest? Jeśli look at the source for toArray zobaczysz:

val result = new Array[B](size) 

Ten konstruktor Array wymaga ClassManifest. Zobacz odpowiedź Easy Angel na dokumentację tego. Oto przykład, wykazując je w 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) 

Więc w zasadzie, trzeba napisać T: ClassManifest ponieważ Scala potrzebuje ClassManifest aby stworzyć nową tablicę.

10

Metoda toArray tworzy nową tablicę z elementami typu T. I 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 używa JVM rodzimych tablic jako wdrożenie do Array. Takie można utworzyć tylko ze znanym typem.

Ponieważ firma Sun zdecydowała się tworzyć starsze artefakty, typy ogólne są usuwane i nie są już obecne w kodzie bajtowym. Oznacza to, że w czasie wykonywania, Array[T] nie zna już wartości T i dlatego maszyna wirtualna nie może utworzyć tablicy. Istnieje kilka bardziej skomplikowanych pułapek, gdy próbujesz być kompatybilny z Javą 1.4 i wprowadzasz generyczne na tablicy (nie dostaję też całej głębi), ale najważniejsze jest to, że tablice JVM/Java nie są generyczne; Scala pomaga nam w ogólnej abstrakcji.

Klasy, o której wspominasz, to w gruncie rzeczy hack. Zapewnia statyczną informację o typie, która jest dostępna, gdy tablica ma zostać utworzona przez żądanie tego niejawnego parametru. W praktyce każda metoda/funkcja, która tworzy tablicę parametru typu, musi żądać niejawnego manifestu, a wszystkie jego nazwy i tak dalej, aż do momentu, w którym typ jest znany (statycznie).

+0

"prawdopodobnie dlatego, że obiekty są rzeczywiście umieszczane na stercie po kolei" Nie, nie są. Tablice obiektów przechowują referencje sekwencyjnie, a nie same obiekty. (To oczywiście nie dotyczy tablic prymitywów.) –

+1

Edytowane na rzecz bardziej niejasnego rozumowania. Śruba spuścizna, poważnie ... – Raphael

Powiązane problemy