2011-01-31 60 views
11

定義如下代碼:爲什麼ClassManifest需要Array而不是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 

注意的testFunctestFunc1最終輸出是相同的。

List版本怎麼不需要ClassManifest

回答

10

Java中的數組未被類型擦除,特別是,在JVM級別,Array[Int]Array[Object]不同。

對於任何其他類,類型參數將被擦除到Object,因此List[Int]List[Object]在JVM級別具有相同的表示形式。

2

簡短的回答是,因爲這是方法如何defined in the API

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

如果您在def test[T:ClassManifest]在代碼中留下關閉:ClassManifest,那麼所有的編譯器知道的是,它有一些未知類型T,因此編譯器無法找到該類型的ClassManifest

爲什麼代碼需要ClassManifest?如果您look at the source for 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

10

方法toArray使用T類型的元素創建新陣列。而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

斯卡拉使用JVM本地數組作爲實施Array。這隻能用已知類型創建。

由於Sun決定創建遺留工件,所以泛型類型被擦除並且不再存在於字節代碼中。這意味着在運行時,Array[T]不再知道T的值,因此VM不能創建陣列。當你嘗試與Java 1.4兼容並且在數組中引入泛型時(我沒有得到整個深度),你會得到一些更復雜的陷阱,但底線是:JVM/Java的數組不是通用的; Scala幫助我們進行泛型抽象。

你提到的類清單實質上是一種黑客。它通過要求這個隱式參數來創建數組時,靜態地確保該類型信息可用。在實踐中,任何創建一個類型參數數組的方法/函數都必須要求一個隱式清單,並且其所有的被調用者等等都是已知的(靜態的)類型。

+0

「大概是因爲對象是居然把上堆順序」不,他們不是。對象數組依次存儲引用,而不是對象本身。 (當然,這不適用於基元數組。) – 2011-01-31 21:03:14

+1

編輯贊成更模糊的推理。螺絲遺產,認真... – Raphael 2011-01-31 23:07:12

相關問題