注:本文為譯文,原文是Scala官方文檔中的1篇,這里翻譯出來作為本系列的1篇文章。原文鏈接: http://docs.scala-lang.org/overviews/reflection/typetags-manifests.html。 本文原文出處: http://blog.csdn.net/bluishglc/article/details/52596696 嚴(yán)禁任何情勢的轉(zhuǎn)載,否則將拜托CSDN官方保護(hù)權(quán)益!
猶如所有其他的JVM語言,Scala(在范型中)的類型信息在編譯期都被擦除,這意味著如果你在運行時去檢測1些實例的類型,你可能沒法獲得全部的類型信息,即便Scala的編譯器在編譯時期都知道這些信息。
像scala.reflect.Manifest1樣, TypeTags可以被認(rèn)為是攜帶了編譯期全部類型信息到運行期的對象,例如,TypeTag[T]封裝了在編譯時期肯定的T的實際類型信息,而這些信息可以在運行時期使用(為表述更清晰,本句翻譯在原文基礎(chǔ)上做了調(diào)劑),你要記住,TypeTags可以視作Scala 2.10之前的Manifest類的1個更加豐富的替換者,它完全和Scala的反射系統(tǒng)繼承在了1起。
Scala有3種不同類型的TypeTags
猶如Manifest,TypeTag總是由編譯器生成的,可以通過3種方式獲得。
你可以通過typeTag方法直接獲得某個特定類型的TypeTag,typeTag方法在Universe。
例如, 獲得Int型的1個TypeTag:
import scala.reflect.runtime.universe._
val tt = typeTag[Int]
或獲得String型的1個ClassTag:
import scala.reflect._
val ct = classTag[String]
這些方法基于給定的類型參數(shù)T構(gòu)建了TypeTag[T] 或ClassTag[T]的實例(譯者注:在編譯期由編譯器自動完成)。
像Manifest1樣,你實際上可以要求編譯器去生成1個TypeTag, 這可以通過指定1個TypeTag[T]的隱式的“跡象”(evidence)參數(shù)來實現(xiàn)。如果編譯器在編譯期間沒能找到匹配的隱式值,編譯器就會自動創(chuàng)建1個TypeTag[T]的實例(譯者注:固然,這時候類型T已肯定)。
注意:在方法和類上使用TypeTag[T],ClassTag[T]或WeakTypeTag[T]的1個隱式參數(shù)的做法是很常見的。
例如,我們可以寫1個接受任何類型做參數(shù)的方法,然后通過TypeTag打印這個參數(shù)的類型信息:
import scala.reflect.runtime.universe._
def paramInfo[T](x: T)(implicit tag: TypeTag[T]): Unit = {
val targs = tag.tpe match { case TypeRef(_, _, args) => args }
println(s"type of $x has type arguments $targs")
}
在這里,我們寫了1個范型方法paramInfo,同時提供了1個隱式參數(shù)(implicit tag: TypeTag[T])。然后我們就能夠直接獲得類型信息(TypeTag的tpe方法返回的是T的Type實例)。
(譯者注:上述方法的意圖是:如果T是援用類型(TypeRef),獲得它的類型參數(shù)列表。)
我們可以這樣使用我們的paramInfo方法。
scala> paramInfo(42)
type of 42 has type arguments List()
scala> paramInfo(List(1, 2))
type of List(1, 2) has type arguments List(Int)
(譯者注:targs 本身是1個List,42是簡單類型,沒有類型參數(shù),所以是1個空的List,List(1, 2)明顯是1個List[Int],所以它的類型參數(shù)是Int)
1種更加簡單地實現(xiàn)上述目標(biāo)的做法是使用1個類型參數(shù)的Context Bound。不同于提供1個獨立的隱式參數(shù),你可以像下面這樣把TypeTag簡單的加到類型參數(shù)列表中:
def myMethod[T: TypeTag] = ...
基于給定的上下文邊界[T: TypeTag],編譯器會簡單地生成1個TypeTag[T]的隱式參數(shù),然后把方法重寫成像前1個帶隱式參數(shù)的例子那樣。
上面使用上下文邊界的例子會被(編譯器)重寫為:
import scala.reflect.runtime.universe._
def paramInfo[T: TypeTag](x: T): Unit = {
val targs = typeOf[T] match { case TypeRef(_, _, args) => args }
println(s"type of $x has type arguments $targs")
}
scala> paramInfo(42)
type of 42 has type arguments List()
scala> paramInfo(List(1, 2))
type of List(1, 2) has type arguments List(Int)
WeakTypeTag[T]泛化了TypeTag[T]。不像1個常規(guī)的TypeTag,它的類型描寫組件可以指向類型參數(shù)或抽象類型,但是,WeakTypeTag[T]總是盡量的具體化。比如,如果被援用類型參數(shù)或抽象類型的類型標(biāo)簽是可用的,它們通常會被嵌入到WeakTypeTag[T]的具體類型中去。讓我們繼續(xù)上面的例子:
import scala.reflect.runtime.universe._
def weakParamInfo[T](x: T)(implicit tag: WeakTypeTag[T]): Unit = {
val targs = tag.tpe match { case TypeRef(_, _, args) => args }
println(s"type of $x has type arguments $targs")
}
scala> def foo[T] = weakParamInfo(List[T]())
foo: [T]=> Unit
scala> foo[Int]
type of List() has type arguments List(T)
寬泛地說,TypeTags相當(dāng)于2.10版本之前的scala.reflect.Manifests是相1致的。而scala.reflect.ClassTag相當(dāng)于cala.reflect.ClassManifest,scala.reflect.api.TypeTags#TypeTag最貼近scala.reflect.Manifest。其他2.10版本之前的Manifest類型就沒有對應(yīng)的”Tag”類型了。
在Scala 2.10里,scala.reflect.ClassManifests不再被推薦使用!scala.reflect.Manifest也將被TypeTags和ClassTag所代表的風(fēng)格所取代。