zoukankan      html  css  js  c++  java
  • 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
    
    
      }
    }
  • 相关阅读:
    hadoop2.2.0伪分布式搭建
    HBase详解
    Hbase常用命令
    hbase读取数据原理
    约瑟夫环
    哈希表设计
    哈夫曼树的建立
    TCP的连接和释放
    什么是Kmp算法?
    什么是https
  • 原文地址:https://www.cnblogs.com/LXL616/p/11136020.html
Copyright © 2011-2022 走看看