16.Scala-高级类型

第16章 高级类型

16.1 类型与类的区别

  在 Java 里,一直到 jdk1.5 之前,我们说一个对象的类型(type),都与它
的 class 是一一映射的,通过获取它们的 class 对象,比
如 String.class, int.class, obj.getClass() 等,就可以判断它们的类型(type)是不
是一致的。
  而到了 jdk1.5 之后,因为引入了泛型的概念,类型系统变得复杂了,并
且因为 jvm 选择了在运行时采用类型擦拭的做法(兼容性考虑),类型已经不能
单纯的用 class 来区分了,比如 List和 List的 class 都是 Class,然而两者类
型(type)却是不同的。泛型类型的信息要通过反射的技巧来获取,同时 java 里
增加了 Type 接口来表达更泛的类型,这样对于 List这样由类型构造器和
类型参数组成的类型,可以通过 Type 来描述;它和 List类型的对应的 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),并不是对象实例。

16.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]。

scala> class A
defined class A

scala> val a = new A
a: A = A@663bb8ef

scala> a.getClass
res2: Class[_ <: A] = class A

scala> classOf[A]
res3: Class[A] = class A

scala> typeOf[A]
<console>:15: error: not found: value typeOf
       typeOf[A]
       ^

scala> import scala.reflect.runtime.universe._
import scala.reflect.runtime.universe._

scala> typeOf[A]
res5: reflect.runtime.universe.Type = A
 

16.3 单例类型

singleton type 是所有实例都可以有的。
scala> object A
defined object A
scala
> A.getClass res2: Class[_ <: A.type] = class A$
scala
> typeOf[A.type] res0: reflect.runtime.universe.Type = A.type
对于这种单例,它的类型与它的类不同,要用 A.type 来表示。
这有点怪,通常我们不会用它,比如下面的方式都多此一举:
scala> val a : A.type = A
a: A.type = A$@e044b4a

scala> def foo() : A.type = A
foo: ()A.type
一方面因为 scala 有类型推导的功能,另一方面,因为单例是唯一的,
A.type 类型只有唯一的实例 A(排除 null),我需要的话直接用 A 就好了。
不过我们讨论的话题重点是 singleton type,想象一下 A 是一个对象实
例,是否对任何实例 x 都存在一个 x.type 这样的类型呢?
scala> class A
scala> val a = new A
scala
> typeOf[a.type] res0: reflect.runtime.universe.Type = a.type
真的存在。再用这个类型声明一个变量看看:
scala> val x:a.type = a
x: a.type = A@6738694b
灵的,如果赋一个非 a 的实例呢?
scala> val x:a.type = a2
<console>:13: error: type mismatch;
 found : a2.type (with underlying type A)
 required: a.type
scala
> typeOf[a.type] == typeOf[A] // a.type 与 A 不是同一个类型 res2: Boolean = false
scala> typeOf[a.type] == typeOf[a2.type] // a.type 与 a2.type 也不同 res1: Boolean = false
scala> typeOf[a.type] <:< typeOf[A] // a.type 是 A 类型的子类型 res5: Boolean = true
  看到了,a.type 与 a2.type 是不同的类型!a.type 也是单例类型,它也只
有唯一的实例: a (排除 null)
  所有的对象实例都有一个 x.type 的单例类型,它只对应当前对象实例。
这么做有什么意义呢?
  单例类型和单例对象不一样,他是一种类型,每一个引用 v 都有一个单
例类型 v.type,他只有两个可能的值:v 和 null,他表示的时候当前对象的类
型。
单例类型用的最多的是在链式调用中,如下:
class A {
  private var name:String=null
  def setName(name:String)={
    this.name = name
    this // 返回调用对象
  }
}


class B extends A{
  private var sex:String = null
  def setAge(sex:String)={
    this.sex=sex
    this
  }
}

object Main1 extends App{
  val a:A = new A
  val b:B = new B
//  b.setName("WangBa").setAge("woman") // 无法执行
  print(b)
  b.setAge("WangBa").setName("woman") // 可以执行
  print(b) //unit16.B@566776adunit16.B@566776ad

}
  可以发现先执行 setName 后执行 setAge 是不能运行的,原因是调用
setName 时返回 this,scala 推荐为 A 类,可以通过单例类型来修改 setName
方法:
def setName(name:String):this.type ={
  this.name = name
  this // 返回调用对象
}
这样一来,Scala 将返回类型就是 b.type.
 
 
 
 

16.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里没有路径依赖类型的概念,比较简化。

16.5 类型别名

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

import scala.collection.mutable.HashMap

class Document {
  import scala.collection.mutable._
  type Index = HashMap[String, (Int, Int)]

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

}


object Main extends App{
  type Index = HashMap[String, (Int, Int)]

  val d = new Document
  val m = new HashMap[String, (Int, Int)]
  val m1 = new Index
  d.play(m)
  d.play(m1)

}

16.6 结构类型

 结构类型是指一组关于抽象方法、字段和类型的规格说明,你可以对任何具备append方法的类的实例调用appendLines方法,

这种方式比定义特质更加灵活,是通过反射进行调用的:

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)
  }
}

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

16.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

16.8 中置类型

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

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

16.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 { println(outer.v1) // 用outer表示外部类,相当于Outer.this } }

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

只是this的别名,所以不允许用this做this的别名。

16.10 运行时反射

scala编译器会将scala代码编译成JVM字节码,编译过程中会擦除scala特有的一些类型信息,

在scala-2.10以前,只能在scala中利用java的反射机制,

但是通过java反射机制得到的是只是擦除后的类型信息,

并不包括scala的一些特定类型信息。从scala-2.10起,

scala实现了自己的反射机制,我们可以通过scala的反射机制得到scala的类型信息。
给定类型或者对象实例,通过scala运行时反射,可以做到:

1)获取运行时类型信息;

2)通过类型信息实例化新对象;

3)访问或调用对象的方法和属性等。

16.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))

16.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)
  }
}

16.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)
    //得到Method的Mirror
    val myPrintMethod = ru.typeOf[Person].decl(ru.TermName("myPrint")).asMethod
    //通过Method的Mirror索取方法
    val myPrint = instanceMirror.reflectMethod(myPrintMethod)
    //运行myPrint方法
    myPrint()

    //得到属性Field的Mirror
    val nameField = ru.typeOf[Person].decl(ru.TermName("name")).asTerm
    val name = instanceMirror.reflectField(nameField)
    println(name.get)

  }
}

 笔记:

package unit16

class Person(name:String, age: Int) {
  def myPrint() = {
    println(name + "," + age)
  }
}

object PersonMain extends App{
  override def main(args: Array[String]): Unit = {
    /*
    Scala通过反射,实例化对象
     */
    //得到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) //unit16.Person@7cbc3762


    /*
    Scala通过反射,调用对象
     */
    val instanceMirror = mirror.reflect(p)
    //得到Method的Mirror
    val myPrintMethod = ru.typeOf[Person].decl(ru.TermName("myPrint")).asMethod
    //通过Method的Mirror索取方法
    val myPrint = instanceMirror.reflectMethod(myPrintMethod)
    //运行myPrint方法
    myPrint() //Mike,1



    /*
    Scala通过反射,访问属性
     */
    val nameField = ru.typeOf[Person].decl(ru.TermName("name")).asTerm
    val name = instanceMirror.reflectField(nameField)
    println(name.get) //Mike


  }
}
原文地址:https://www.cnblogs.com/LXL616/p/11136020.html