Scala的反射机制
Manifest & ClassManifest
Manifest是在编译时捕捉的,编码了“捕捉时”所致的类型信息。然后就可以在运行时检查和使用类型信息,但是manifest只能捕捉当Manifest被查找时在隐式作用域里的类型。1
def first[A : ClassManifest](x:Array[A]) = Array(x(0))
ClassTag & TypeTag
ClassTag[T]保存了被泛型擦除后的原始类型T,提供给运行时的
1
2scala> def mkArray[T : ClassTag](elems: T*) = Array[T](elems: _*)
mkArray: [T](elems: T*)(implicit evidence$1: scala.reflect.ClassTag[T])Array[T]TypeTag则保存所有具体的类型
1
2
3
4
5
6
7
8
9import 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")
}
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)
可以看到,获取到的类型是具体的类型,而不是被擦除后的类型List(Any)
scala在2.10里却用TypeTag替代了Manifest,用ClassTag替代了ClassManifest,原因是在路径依赖类型中,Manifest存在问题:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15scala> class Foo{class Bar}
scala> def m(f: Foo)(b: f.Bar)(implicit ev: Manifest[f.Bar]) = ev
scala> val f1 = new Foo;val b1 = new f1.Bar
scala> val f2 = new Foo;val b2 = new f2.Bar
scala> val ev1 = m(f1)(b1)
ev1: Manifest[f1.Bar] = Foo@681e731c.type#Foo$Bar
scala> val ev2 = m(f2)(b2)
ev2: Manifest[f2.Bar] = Foo@3e50039c.type#Foo$Bar
scala> ev1 == ev2 // they should be different, thus the result is wrong
res28: Boolean = true
ev1 不应该等于 ev2 的,因为其依赖路径(外部实例)是不一样的。
还有其他因素,所以在2.10版本里,使用 TypeTag 替代了 Manifest
TypeTag:由编辑器生成,只能通过隐式参数或者上下文绑定获取
可以有两种方式获取:1
2
3
4
5
6
7
8
9
10
11
12scala> import scala.reflect.runtime.universe._
import scala.reflect.runtime.universe._
//使用typeTag
scala> def getTypeTag[T:TypeTag](a:T) = typeTag[T]
getTypeTag: [T](a: T)(implicit evidence$1: reflect.runtime.universe.TypeTag[T])reflect.runtime.universe.TypeTag[T]
//使用implicitly 等价的
//scala>def getTypeTag[T:TypeTag](a:T) = implicitly[TypeTag[T]]
scala> getTypeTag(List(1,2,3))
res0: reflect.runtime.universe.TypeTag[List[Int]] = TypeTag[List[Int]]
通过TypeTag的tpe方法获得需要的Type(如果不是从对象换取Type 而是从class中获得 可以直接用 typeOf[类名])
反射获取TypeTag和ClassTag
JavaCodeExample
String———->Calss————–>Manifest——–>TypeTag
Class.forName ManifestFactory.classType scala.reflect.runtime.universe.manifestToTypeTag1
2
3
4
5
6
7import scala.reflect.runtime.universe
import scala.reflect.ManifestFactory
val className = "java.lang.String"
val mirror = universe.runtimeMirror(getClass.getClassLoader)
val cls = Class.forName(className)
val t = universe.manifestToTypeTag(mirror, ManifestFactory.classType(cls))classOf与getClass方法的差异
getClass 方法得到的是 Class[A]的某个子类,而 classOf[A] 得到是正确的 Class[A],但是去比较的话,这两个类型是equals为true的
classOf获取运行时的类型。classOf[T] 相当于 java中的T.class; 而getClass:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17val listClass = classOf[List[_]]
* // listClass is java.lang.Class[List[_]] = class scala.collection.immutable.List
val mapIntString = classOf[Map[Int,String]]
* // mapIntString is java.lang.Class[Map[Int,String]] = interface scala.collection.immutable.Map
* }}}
scala> class A
scala> val a = new A
scala> a.getClass
res2: Class[_ <: A] = class A
scala> classOf[A]
res3: Class[A] = class A
scala> a.getClass == classOf[A]
res13: Boolean = true
这种细微的差别,体现在类型赋值时,因为java里的 Class[T]是不支持协变的,所以无法把一个 Class[_ < : A] 赋值给一个 Class[A]1
2scala> val c:Class[A] = a.getClass
<console>:9: error: type mismatch;
类(class)与类型(type)是两个不一样的概念
(在java里因为早期一直使用class表达type,并且现在也延续这样的习惯);类型(type)比类(class)更”具体”,任何数据都有类型。类是面向对象系统里对同一类数据的抽象,在没有泛型之前,类型系统不存在高阶概念,直接与类一一映射,而泛型出现之后,就不在一一映射了。比如定义class List[T] {}, 可以有List[Int] 和 List[String]等具体类型,它们的类是同一个List,但类型则根据不同的构造参数类型而不同。
类型一致的对象它们的类也是一致的,反过来,类一致的,其类型不一定一致。
1 | scala> classOf[List[Int]] == classOf[List[String]] |