2010-07-09 93 views
116

由於Scala 2.7.2有一個名爲Manifest的東西,它是Java類型擦除的解決方法。但Manifest如何正確工作以及爲什麼/何時需要使用它?什麼是Scala中的清單,什麼時候需要它?

博客文章Manifests: Reified Types由豪爾赫·奧爾蒂斯介紹了一些,但它並沒有解釋如何與context bounds一起使用。

另外,什麼是ClassManifest,與Manifest有什麼區別?

我有一些代碼(一個較大的程序的一部分,不能輕易將其包含在這裏),它有一些關於類型擦除的警告;我懷疑我可以通過使用清單來解決這些問題,但我不確定如何。

+2

關於Manifest/ClassManifest差異的討論已在郵件列表中進行了討論,請參閱http://scala-programming-language.1934581.n4.nabble.com/What-s-the-difference-between-ClassManifest-和 - Manifest-td2125122.html – 2010-07-09 23:23:57

+0

另請參見:[Scala:什麼是TypeTag,我該如何使用它?](http://stackoverflow.com/questions/12218641/scala-what-is-a-typetag-and如何使用它) – Jesper 2015-10-09 10:31:49

回答

174

編譯器知道關於類型的更多信息,而不是JVM運行時可以輕鬆表示的信息。 Manifest是編譯器在運行時向代碼發送間維信息的一種方式,用於說明丟失的類型信息。

這與Kleptonians如何在化石記錄中留下編碼信息和人類的「垃圾」DNA相似。由於光速和重力場的限制,它們無法直接通信。但是,如果你知道如何調諧他們的信號,你可以以你無法想象的方式獲益,從決定午餐吃什麼或吃哪個樂透號碼。

如果一個清單會在不瞭解更多細節的情況下有利於您看到的錯誤,那麼目前尚不清楚。

Manifests的一個常見用法是讓您的代碼根據集合的靜態類型行爲不同。例如,如果你想從其他類型的列表的區別對待列表[字符串]什麼:

def foo[T](x: List[T])(implicit m: Manifest[T]) = { 
    if (m <:< manifest[String]) 
     println("Hey, this list is full of strings") 
    else 
     println("Non-stringy list") 
    } 

    foo(List("one", "two")) // Hey, this list is full of strings 
    foo(List(1, 2)) // Non-stringy list 
    foo(List("one", 2)) // Non-stringy list 

基於反射的解決方案,這可能會涉及到檢查列表中的每個元素。

結合上下文,似乎最適合於使用階型類,並深受Debasish戈什這裏解釋: http://debasishg.blogspot.com/2010/06/scala-implicits-type-classes-here-i.html

上下文範圍也可以只使該方法的簽名更具可讀性。例如,上述功能可以使用上下文範圍,像這樣重新編寫:

def foo[T: Manifest](x: List[T]) = { 
    if (manifest[T] <:< manifest[String]) 
     println("Hey, this list is full of strings") 
    else 
     println("Non-stringy list") 
    } 
+13

Upvoted用例。希望我對外部Kleptonian部分進行第二次投票。 – huynhjl 2010-07-09 15:13:35

+8

啊哈,這是來自Scala編譯器的一個祕密的內部消息... ;-) – Jesper 2010-07-10 06:05:06

+1

氪星人?無論如何,偉大的隱喻! – kirhgoff 2013-11-15 09:52:26

25

不是一個完整的答案,但對於ManifestClassManifest之間的區別,你可以找到在Scala 2.8 Array paper一個例子:

唯一剩下的問題是如何實現通用陣列創建。與Java不同,Scala允許創建新實例Array[T],其中T是一個類型參數。鑑於Java中不存在統一的數組表示,這怎麼能夠實現呢?

要做到這一點的唯一方法是需要額外的運行時信息,其中描述了類型T。斯卡拉2.8有一個新的機制,這就是所謂的ManifestManifest[T]類型的對象提供了有關類型T的完整信息。
Manifest值通常以隱式參數傳遞;編譯器知道如何爲靜態已知類型T構造它們。

還存在着命名ClassManifest一個較弱形式其可以從知道剛剛頂層類的類型,來構造而不必知道它的所有參數類型
正是這種類型的運行時信息是創建數組所必需的。

實施例:

人們需要通過使ClassManifest[T]到 方法作爲隱式參數來提供這樣的信息:

def tabulate[T](len:Int, f:Int=>T)(implicit m:ClassManifest[T]) = { 
    val xs = new Array[T](len) 
    for (i <- 0 until len) xs(i) = f(i) 
    xs 
} 

作爲簡寫形式,一上下文bound1可以用在類型參數T上,而不是

(見本SO question for illustration

,得到:

def tabulate[T: ClassManifest](len:Int, f:Int=>T) = { 
    val xs = new Array[T](len) 
    for (i <- 0 until len) xs(i) = f(i) 
    xs 
} 

當調用的類型製表如Int,或String,或List[T],Scala編譯器可以創建一個類清單作爲隱式參數傳遞給列表。

22

一個清單是爲了具體化泛型類型獲取類型擦除在JVM上運行(不支持泛型)。然而,他們有一些嚴重的問題:它們太簡單了,並且無法完全支持Scala的類型系統。他們因此在Scala 2中棄用。10,並替換爲TypeTag(這實質上是Scala編譯器本身用來表示類型的因素,因此完全支持Scala類型)。有關差異的詳細信息,請參閱:

換句話說

當你需要它?

之前2013-01-04,when Scala 2.10 was released

+0

它尚未被棄用(但將是),因爲斯卡拉反射在2.10仍然是實驗性的。 – Keros 2013-04-22 10:23:06

+0

在2013-01-04之前,或者如果您使用依賴它的API。 – 2013-12-19 23:16:26

1

我們也scala來源CHCK出manifestManifest.scala),我們可以看到:

Manifest.scala: 
def manifest[T](implicit m: Manifest[T])   = m 
與問候

所以到下面的示例代碼:

def foo[A](somelist: List[A])(implicit m: Manifest[A]): String = { 
    if (m <:< manifest[String]) { 
    "its a string" 
    } else { 
    "its not a string" 
    } 
} 

,我們可以看到,manifestfunction搜索隱含m: Manifest[T],它滿足您在我們的示例代碼中提供的type parameter這是manifest[String]。所以,當你調用是這樣的:

if (m <:< manifest[String]) { 

要檢查,如果你在你的函數定義的當前implicit mmanifest[String]類型,作爲manifest的是它會搜索特定manifest[String],它manifest[T]類型的函數會發現是否有這樣一種隱含的。

相關問題