如果当初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
。