如果當初Scala沒有選擇基於Jvm,那么Scala是否還能取得今天的成就嗎?Jvm為Scala帶了穩健強大的性能,同時也無法避免類型擦除的約束。
作為Jvm上的先進語言,Scala在生成字節碼時,編譯器附加了額外的類型信息,及時class的泛型參數被擦除了,scala仍然可以獲取泛型信息。
主要存在三種api:
TypeTag
,可獲取一個類型的全部信息,包括高階類型,比如List[List[List[String]]]
類型。ClassTag
,可獲取類型的部分信息。ClassTag[List[List[String]]]
,僅可得到類型擦除后的類的類型,也就是scala.collection.immutable.List
。WeakTypeTag
是可以用於獲取抽象類型,比如def foo[A]=List.empty[A]
,想要獲取這個抽象類型A
,需要使用WeakTypeTag
。
➜ scala
Welcome to Scala 2.12.2 (Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0_141).
Type in expressions for evaluation. Or try :help.
scala> import scala.reflect.runtime.universe._
import scala.reflect.runtime.universe._
scala> val tt=typeTag[List[List[String]]]
tt: reflect.runtime.universe.TypeTag[List[List[String]]] = TypeTag[scala.List[scala.List[String]]]
scala> import scala.reflect._
import scala.reflect._
scala> val ct=classTag[Map[String,Int]]
ct: scala.reflect.ClassTag[Map[String,Int]] = scala.collection.immutable.Map
scala> ct.runtimeClass
res1: Class[_] = interface scala.collection.immutable.Map
利用編譯器獲取類型信息
之前說過,scala編譯器在編譯器全部保存了相關的類型信息,僅僅需要借助隱式參數由編譯器傳入即可。
import scala.reflect.runtime.universe._
def typeInfo[T](x: T)(implicit tag: TypeTag[T]) = tag.tpe match {
case TypeRef(preType: Type, symbol: Symbol, typeParams: List[Type]) =>
println(s"preType.typeSymbol : ${preType.typeSymbol}")
println(s"preType : ${preType}")
println(symbol.fullName)
println(typeParams)
}
typeInfo(Map("1"->1))
//打印如下::
preType.typeSymbol : package immutable
preType : scala.collection.immutable.type
scala.collection.immutable.Map
List(java.lang.String, Int)
在以上程序中,preType
是x對象的前綴路徑的類型,這里的scala.collection.immutable
是一個package。symbol
就是參數x的符號信息,typeParams
是x對應的類型的類型參數,對於Map[String,Int]
,那么其類型參數有java.lang.String
和Int
。
一句話解釋scala的反射機制:編譯時期額外類型信息,此類型信息可以用編碼顯式給出,也可以用編譯器推斷。以下的例子可以說明:
def getType[T](x: T)(implicit tag: TypeTag[T]): Type = tag.tpe
現有一個函數getType(x)
,可以此獲取某個對象x
的類型信息。顯式給出類型為Any
,編譯器則傳入隱式tag:TypeTag[Any]
對象。
println(getType[Any](List(1, 2, 3)))
//打印 Type[Any]
println(getType(List(1, 2, 3)))
//打印 Type[List[Int]]
Type之間的比較操作
type之間的比較主要有三種類型:
- 比較兩個類型是否是繼承關系
- 判斷兩個類型之間的相等性
- 給定某一確定的類型的成員方法或者屬性
現在有兩個trait,分別是A
和B
,且A
是B
的父級。
trait A
trait B extends A
val aType = typeOf[A]
val bType = typeOf[B]
println(aType =:= bType) //false
println(aType <:< bType) //false
println(bType <:< aType) //true
println(aType <:< aType) //true
對於給定的兩個Type
實例,<:<
方法可以用於比較兩個類型是否具有父子類型關系,作用與Java中的isInstance
類似。
當使用==
比較兩個類型時,類型別名與實際類型被認為是不同類型,而=:=
會用最根本的類型去比較。
type PersonName=String
val t1= typeOf[PersonName]
val t2= typeOf[String]
t1 == t2 //false
t1 =:= t2 //true
TypeTag
可以等效的看作為scala2.10版本以前的Manifest
,ClassTag
也可以等效的看作為scala2.10版本之前的ClassManifest
。