Scala 深刻淺出實戰經典 第46講: ClassTag 、Manifest、ClasMainifest TagType實戰

王家林親授《DT大數據夢工廠》大數據實戰視頻 Scala 深刻淺出實戰經典(1-64講)完整視頻、PPT、代碼下載:
百度雲盤:http://pan.baidu.com/s/1c0noOt6
騰訊微雲:http://url.cn/TnGbdC
360雲盤:http://yunpan.cn/cQ4c2UALDjSKy 訪問密碼 45e2
技術愛好者尤爲是大數據愛好者 能夠加DT大數據夢工廠的qq羣java

DT大數據夢工廠① :462923555
DT大數據夢工廠②:437123764
DT大數據夢工廠③ :418110145數組

微信公衆帳號: DT_Spark
王家林老師微信號: 18610086859
王家林老師QQ: 1740415547
王家林老師郵箱: 18610086859@126.com微信

本視頻由王家林老師, 親自講解, 徹底經過代碼實戰把您帶人大數據的時代.大數據

package com.parllay.scala.type_parameterizitor

/**
* Created by richard on 15-7-30.
* 第46講: ClassTag 、Manifest、ClasMainifest TagType實戰
*/
object Manifest_Class {

def main(args: Array[String]) {
/**
Manifest是scala2.8引入的一個特質,用於編譯器在運行時也能獲取泛型類型的信息。
在JVM上,泛型參數類型T在運行時是被「擦拭」掉的,編譯器把T看成Object來對待,
因此T的具體信息是沒法獲得的;爲了使得在運行時獲得T的信息,
scala須要額外經過Manifest來存儲T的信息,並做爲參數用在方法的運行時上下文。

def test[T] (x:T, m:Manifest[T]) { ... }
有了Manifest[T]這個記錄T類型信息的參數m,在運行時就能夠根據m來更準確的判斷T了。
但若是每一個方法都這麼寫,讓方法的調用者要額外傳入m參數,很是不友好,且對方法的設計是一道傷疤。
好在scala中有隱式轉換、隱式參數的功能,在這個地方能夠用隱式參數來減輕調用者的麻煩。

這裏給出了一個例子摘自 StackOverflow :

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
隱式參數m是由編譯器根據上下文自動傳入的,好比上面是編譯器根據 "one","two" 推斷出 T 的類型是 String,
從而隱式的傳入了一個Manifest[String]類型的對象參數,使得運行時能夠根據這個參數作更多的事情。

不過上面的foo 方法定義使用隱式參數的方式,仍顯得囉嗦,因而scala裏又引入了「上下文綁定」,
回顧一下以前 context bounds,使得foo方法

def foo[T](x: List[T]) (implicit m: Manifest[T])
能夠簡化爲:

def foo[T:Manifest] (x: List[T])
這個機制原由是scala2.8對數組的從新設計而引入的,本來只是爲了解決數組的問題(後續介紹數組類型),
後續被用在更多方面。在引入Manifest的時候,還引入了一個更弱一點的ClassManifest,
所謂的弱是指類型信息不如Manifest那麼完整,主要針對高階類型的狀況:

scala> class A[T]

scala> val m = manifest[A[String]]

scala> val cm = classManifest[A[String]]
根據規範裏的說法,m的信息是完整的:m: Manifest[A[String]] = A[java.lang.String],
而 cm 則只有 A[_] 即不包含類型參數的信息,
但我在2.10下驗證cm也是:cm: ClassManifest[A[String]] = A[java.lang.String]

在獲取類型其類型參數時也是都包含的:

scala> m.typeArguments
res8: List[scala.reflect.Manifest[_]] = List(java.lang.String)

scala> cm.typeArguments
res9: List[scala.reflect.OptManifest[_]] = List(java.lang.String)
後來從這個帖子裏,看到一些案例,只在:

scala> class A[B] // 注意在2.10下與帖子中不一致,A[+B] 也是一樣的效果
defined class A

scala> manifest[A[_]]
res15: scala.reflect.Manifest[A[_]] = A[_ <: Any]

scala> classManifest[A[_]]
res16: scala.reflect.ClassTag[A[_]] = A[<?>]
到這裏咱們基本明白了 Manifest 與 ClassManifest,
不過scala在2.10裏卻用TypeTag替代了Manifest,
用ClassTag替代了ClassManifest,
緣由是在路徑依賴類型中,Manifest存在問題:

scala> class Foo{class Bar}

scala> def m(f: Foo)(b: f.Bar)(implicit ev: Manifest[f.Bar]) = ev

scala> val f1 = new Foo;val b1 = new f1.Bar
scala> val f2 = new Foo;val b2 = new f2.Bar

scala> val ev1 = m(f1)(b1)
ev1: Manifest[f1.Bar] = Foo@681e731c.type#Foo$Bar

scala> val ev2 = m(f2)(b2)
ev2: Manifest[f2.Bar] = Foo@3e50039c.type#Foo$Bar

scala> ev1 == ev2 // they should be different, thus the result is wrong
res28: Boolean = true
ev1 不該該等於 ev2 的,由於其依賴路徑(外部實例)是不同的。

還有其餘因素(見下面的引用),因此在2.10版本里,使用 TypeTag 替代了 Manifest

Manifests are a lie. It has no knowledge of variance (assumes all type parameters are co-variants),
and it has no support for path-dependent, existential or structural types.

TypeTags are types as the compiler understands them. Not 「like」 the compiler understands them,
but 「as」 the compiler understands them — the compiler itself use TypeTags. It’s not 1-to-1, it’s just 1. :-)
*/
}
}
相關文章
相關標籤/搜索