Scala高级类型

1  类型与类的区别

在Java里,一直到jdk1.5之前,我们说一个对象的类型(type),都与它的class是一一映射的,通过获取它们的class对象,比如 String.class, int.class, obj.getClass() 等,就可以判断它们的类型(type)是不是一致的。

而到了jdk1.5之后,因为引入了泛型的概念,类型系统变得复杂了,并且因为jvm选择了在运行时采用类型擦拭的做法(兼容性考虑),类型已经不能单纯的用class来区分了,比如 List<String> 和 List<Integer> 的class 都是 Class<List>,然而两者类型(type)却是不同的。泛型类型的信息要通过反射的技巧来获取,同时java里增加了Type接口来表达更泛的类型,这样对于 List<String>这样由类型构造器和类型参数组成的类型,可以通过 Type 来描述;它和 List<Integer> 类型的对应的Type对象是完全不同的。

在Scala里,类型系统又比java复杂很多,泛型从一开始就存在,还支持高阶的概念(后续会讲述)。所以它没有直接用Java里的Type接口,而是自己提供了一个scala.reflect.runtime.universe.Type(2.10后)

在scala里获取类型信息是比较便捷的:

class A{}

object TypeSyllabus {

  def main(args: Array[String]): Unit = {

    import scala.reflect.runtime.universe._

    println(typeOf[A])

  }

}

同样scala里获取类(Class)信息也很便捷,类似:

class A{}

object TypeSyllabus {

  def main(args: Array[String]): Unit = {

    import scala.reflect.runtime.universe._

    println(typeOf[A])

    println(classOf[A])

  }

}

尖叫提示:注意,typeOf 和 classOf 方法接收的都是类型符号(symbol),并不是对象实例。

2  classOf与getClass的区别

获取Class时的两个方法:classOf 和 getClass

scala> class  A

scala> val a = new A

scala> a.getClass

res2: Class[_ <: A] = class A

scala> classOf[A]

res3: Class[A] = class A

上面显示了两者的不同,getClass方法得到的是 Class[A]的某个子类,而 classOf[A] 得到是正确的 Class[A],但是去比较的话,这两个类型是equals为true的。

这种细微的差别,体现在类型赋值时,因为java里的 Class[T]是不支持协变的,所以无法把一个 Class[_ < : A] 赋值给一个 Class[A]。

3  单例类型

4  类型投影

在scala里,内部类型(排除定义在object内部的),想要表达所有的外部类A实例路径下的B类型,即对 a1.B 和 a2.B及所有的 an.B类型找一个共同的父类型,这就是类型投影,用 A#B的形式表示。

       A#B

        / \

       /   \

     a1.B  a2.B

我们回头来对比一下scala里的类型投影与java里的内部类型的概念,java里的内部类型在写法上是 Outter.Inner 它其实等同于scala里的投影类型 Outter#Inner,java里没有路径依赖类型的概念,比较简化。

5  类型别名

可以通过type关键字来创建一个简单的别名,类型别名必须被嵌套在类或者对象中,不能出现在scala文件的顶层:

class Document {

  import scala.collection.mutable._

  type Index = HashMap[String, (Int, Int)]

}

def play(x: Index): Unit ={}

6  结构类型

结构类型是指一组关于抽象方法、字段和类型的规格说明,你可以对任何具备play方法的类的实例调用play方法,这种方式比定义特质更加灵活,是通过反射进行调用的:

class Structure {

  def play() = println("play方法调用了")

}

 

object HelloStructure {

  def main(args: Array[String]): Unit = {

    type X = {def play(): Unit} //type关键字是把 = 后面的内容命名为别名。

 

    def init(res: X) = res.play //本地方法

 

    init(new {

      def play() = println("Played")

    })

 

    init(new {

      def play() = println("Play再一次")

    })

 

    object A {

      def play() {

        println("A object play")

      }

    }

 

    init(A)

    val structure = new Structure

    init(structure)

  }

}

总结:

结构类型,简单来说,就是只要是传入的类型,符合之前定义的结构的,都可以调用。

7  复合类型

class A extends B with C with D with E

应做类似如下形式解读:

class A extends (B with C with D with E)

T1 with T2 with T3 …

这种形式的类型称为复合类型(compound type)或者也叫交集类型(intersection type)。

也可以通过type的方式声明符合类型:

type X = X1 with X2

8  中置类型

中置类型是一个带有两个类型参数的类型,以中置语法表示,比如可以将Map[String, Int]表示为:

val scores: String Map Int = Map("Fred" -> 42)

9  自身类型

self => 这句相当于给this起了一个别名为self:

class A {

  self =>  //this别名

  val x=2

  def foo = self.x + this.x

}

self不是关键字,可以用除了this外的任何名字命名(除关键字)。就上面的代码,在A内部,可以用this指代当前对象,也可以用self指代,两者是等价的。

它的一个场景是用在有内部类的情况下:

class Outer { outer =>

  val v1 = "here"

  class Inner {

   val v1 = "there"

    println(outer.v1) // 用outer表示外部类,相当于Outer.this

  }

}

对于this别名 self =>这种写法形式,是自身类型(self type)的一种特殊方式。self在不声明类型的情况下,只是this的别名,所以不允许用this做this的别名。

10  运行时反射

scala编译器会将scala代码编译成JVM字节码,编译过程中会擦除scala特有的一些类型信息,在scala-2.10以前,只能在scala中利用java的反射机制,但是通过java反射机制得到的是只是擦除后的类型信息,并不包括scala的一些特定类型信息。从scala-2.10起,scala实现了自己的反射机制,我们可以通过scala的反射机制得到scala的类型信息。

给定类型或者对象实例,通过scala运行时反射,可以做到:1)获取运行时类型信息;2)通过类型信息实例化新对象;3)访问或调用对象的方法和属性等。

10.1  获取运行时类型信息

scala运行时类型信息是保存在TypeTag对象中,编译器在编译过程中将类型信息保存到TypeTag中,并将其携带到运行期。我们可以通过typeTag方法获取TypeTag类型信息。

import scala.reflect.runtime.universe._

val typeTagList = typeTag[List[Int]]//得到了包装Type对象的TypeTag对象

println(typeTagList)

或者使用:

typeOf[List[Int]]//直接得到了Type对象

尖叫提示:Type对象是没有被类型擦除的

我们可以通过typeTag得到里面的type,再通过type得到里面封装的各种内容:

import scala.reflect.runtime.universe._

val typeTagList = typeTag[List[Int]]

println(typeTagList)

println(typeTagList.tpe)

println(typeTagList.tpe.decls.take(10))

10.2  运行时类型实例化

我们已经知道通过Type对象可以获取未擦除的详尽的类型信息,下面我们通过Type对象中的信息找到构造方法并实例化类型的一个对象:

class Person(name:String, age: Int) {

  def myPrint() = {

    println(name + "," + age)

  }

}

 

object PersonMain extends App{

  override def main(args: Array[String]): Unit = {

    //得到JavaUniverse用于反射

    val ru = scala.reflect.runtime.universe

    //得到一个JavaMirror,一会用于反射Person.class

    val mirror = ru.runtimeMirror(getClass.getClassLoader)

    //得到Person类的Type对象后,得到type的特征值并转为ClassSymbol对象

    val classPerson = ru.typeOf[Person].typeSymbol.asClass

    //得到classMirror对象

    val classMirror = mirror.reflectClass(classPerson)

    //得到构造器Method

    val constructor = ru.typeOf[Person].decl(ru.termNames.CONSTRUCTOR).asMethod

    //得到MethodMirror

    val methodMirror = classMirror.reflectConstructor(constructor)

    //实例化该对象

    val p = methodMirror("Mike", 1)

    println(p)

  }

}

10.3  运行时类成员的访问

class Person(name:String, age: Int) {

  def myPrint() = {

    println(name + "," + age)

  }

}

 

object PersonMain extends App{

  override def main(args: Array[String]): Unit = {

    //获取Environment和universe

    val ru = scala.reflect.runtime.universe

    //获取对应的Mirrors,这里是运行时的

    val mirror = ru.runtimeMirror(getClass.getClassLoader)

    //得到Person类的Type对象后,得到type的特征值并转为ClassSymbol对象

    val classPerson = ru.typeOf[Person].typeSymbol.asClass

    //用Mirrors去reflect对应的类,返回一个Mirrors的实例,而该Mirrors装载着对应类的信息

    val classMirror = mirror.reflectClass(classPerson)

    //得到构造器Method

    val constructor = ru.typeOf[Person].decl(ru.termNames.CONSTRUCTOR).asMethod

    //得到MethodMirror

    val methodMirror = classMirror.reflectConstructor(constructor)

    //实例化该对象

    val p = methodMirror("Mike", 1)

    println(p)

 

 

    //反射方法并调用

    val instanceMirror = mirror.reflect(p)

    //得到MethodMirror

    val myPrintMethod = ru.typeOf[Person].decl(ru.TermName("myPrint")).asMethod

    //通过MethodMirror索取方法

    val myPrint = instanceMirror.reflectMethod(myPrintMethod)

    //运行myPrint方法

    myPrint()

 

    //得到属性FieldMirror

    val nameField = ru.typeOf[Person].decl(ru.TermName("name")).asTerm

    val name = instanceMirror.reflectField(nameField)

    println(name.get)

   

  }

}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值