scala的反射

Posted 蒋航的博客

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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

以上是关于scala的反射的主要内容,如果未能解决你的问题,请参考以下文章

java 反射代码片段

通过 Java 反射找到 Scala 类型的伴生对象的可靠方法是啥?

通过 Java 反射找到 Scala 类型的伴生对象的可靠方法是啥?

使用 Scala 反射编译和加载对象

动态代理与Scala反射/Java反射在JavaScalaKotlin中的使用

scala的反射