Scala的类型和反射机制

Scala的反射机制

  1. Manifest & ClassManifest
    Manifest是在编译时捕捉的,编码了“捕捉时”所致的类型信息。然后就可以在运行时检查和使用类型信息,但是manifest只能捕捉当Manifest被查找时在隐式作用域里的类型。

    1
    def first[A : ClassManifest](x:Array[A]) = Array(x(0))
  2. ClassTag & TypeTag

  • ClassTag[T]保存了被泛型擦除后的原始类型T,提供给运行时的

    1
    2
    scala> 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
    9
    import 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
15
scala> 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
12
scala> 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[类名])

  1. 反射获取TypeTag和ClassTag
    JavaCodeExample
    String———->Calss————–>Manifest——–>TypeTag
    Class.forName ManifestFactory.classType scala.reflect.runtime.universe.manifestToTypeTag

    1
    2
    3
    4
    5
    6
    7
    import 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))
  2. 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
17
val 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
2
scala> 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
2
3
4
5
scala> classOf[List[Int]] == classOf[List[String]]
res16: Boolean = true

scala> typeOf[List[Int]] == typeOf[List[String]]
res17: Boolean = false