scala的反射


  如果當初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.StringInt

  一句話解釋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,分別是AB,且AB的父級。

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版本以前的ManifestClassTag也可以等效的看作為scala2.10版本之前的ClassManifest


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM