Scala入门到精通——第二十二节 高级类型 (一)

https://blog.csdn.net/lovehuangjiaju/article/details/47381601

本节主要内容

  1. this.type使用
  2. 类型投影
  3. 结构类型
  4. 复合类型

1. this.type使用


class Person{
 private var name:String=null
 private var age:Int=0
 def setName(name:String)={
     this.name=name
     //返回对象本身
     this
 }
 def setAge(age:Int)={
     this.age=age
     //返回对象本身
     this
 }
 override def toString()="name:"+name+" age:"+age
}

object Main extends App{
  //链式调用
  println(new Person().setAge(18).setName("摇摆少年梦"))
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

当涉及到继承时,这种机制会存在些问题,例如:

class Person{
 private var name:String=null
 private var age:Int=0
 def setName(name:String)={
     this.name=name
     //返回Person对象本身
     this
 }
 def setAge(age:Int)={
     this.age=age
     //返回Person对象本身
     this
 }
 override def toString()="name:"+name+" age:"+age
}

class Student extends Person{
  private var studentNo:String=null
  def setStudentNo(no:String)={
    this.studentNo=no
    this
  }
  override def toString()=super.toString()+" studetNo:"+studentNo
}

object Main extends App{
  //下面的代码会报错
  //value setStudentNo is not a member of cn.scala.xtwy.advancedtype.Person
  println(new Student().setName("john").setAge(22).setStudentNo("2014"))
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30

Student对象在调用setName、setAge方法时,返回的对象类型实质上仍然是Person类型,而Person类型并没有setStudentNo方法,从而编译出错。为解决该问题,可以将setName、setAge方法的返回值设置为:this.type ,代码如下:

class Person{
 private var name:String=null
 private var age:Int=0
 //this.type返回实际类型
 def setName(name:String):this.type={
     this.name=name
     this
 }
 def setAge(age:Int):this.type={
     this.age=age
     this
 }
 override def toString()="name:"+name+" age:"+age
}

class Student extends Person{
  private var studentNo:String=null
  def setStudentNo(no:String)={
    this.studentNo=no
    this
  }
  override def toString()=super.toString()+" studetNo:"+studentNo
}

object Main extends App{
  //println(new Person().setAge(18).setName("摇摆少年梦"))
  println(new Student().setName("john").setAge(22).setStudentNo("2014"))
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28

2. 类型投影

我们知道,Scala中的内部类同类成员一类,只不过它被定义一个类而已,它具有如下的访问创建方式:

class Outter{
  private var x:Int=0
  //内部类Inner
  class Inner{
    def test()=x
  }
}

object TypeProject extends App{
  val outter=new Outter
  //创建内部类的方式,同访问正常的成员变量一样
  val inner=new outter.Inner
  println(inner.test())

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

那Scala语言中不同对象创建的内部类是不是同一个类呢?其实不是,下面的代码就是证明:


import scala.reflect.runtime.universe.typeOf
class Outter{
  private var x:Int=0
  def print(i:Inner)=i
  class Inner{
    def test()=x
  }
}

object TypeProject extends App{
  val outter=new Outter
  val inner=new outter.Inner

  val outter2=new Outter
  val inner2=new outter2.Inner

  //下面的代码编译会失败
  //outter.print(inner2)
  //这是因为不同outter对象对应的内部类成员类型是不一样的
  //这就跟两个类成员的实例它们内存地址不一样类似


  //下面的类型判断会输出false
  //这也进一步说明了它们类型是不一样的
  println(typeOf[outter.Inner]==typeOf[outter2.Inner])

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28

上述代码中Outter类中的def print(i:Inner)=i 成员方法中的参数类型Inner其实相当于def print(i:this.Inner)=i 或def print(i:Outter.this.Inner)=i ,也即它依赖于外部类,整体的话构成了一路径,因为也称为路径依赖类型。

再看下内部类的几种使用情况,它对应几种不同的路径依赖类型: 
(1)类内部本身使用情况

class Outter{
  private var x:Int=0
  //内部使用,相当于
  //private var i:Inner=new Outter.this.Inner
  private var i:Inner=new Inner
  def print(i:Inner)=i
  class Inner{
    def test()=x
  }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

(2)子类使用父类中的内部类

class Outter{
  private var x:Int=0
  def print(i:Inner)=i
  class Inner{
    def test()=x
  }
}
//子类中使用父类中的内部类
class A extends Outter{
  private val i=new A.super.Inner
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

(3)在其它类或对象中使用

object TypeProject extends App{
  val outter=new Outter
  val inner=new outter.Inner

  val outter2=new Outter
  val inner2=new outter2.Inner
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

明白几种路径依赖类型之后,我们可以对类型投影进行说明:类型投影的目的是将外部类Outter中定义的方法def print(i:Inner)=i,它可以接受做任意外部类对象中的Inner类。上面的例子当中outter与outter2中的Inner类型具有共同的父类。如下图所示:

这里写图片描述

代码如下:

import scala.reflect.runtime.universe.typeOf
class Outter{
  private var x:Int=0
  private var i:Inner=new Outter.this.Inner
  //Outter#Inner类型投影的写法
  //可以接受任何outter对象中的Inner类型对象
  def print(i:Outter#Inner)=i
  class Inner{
    def test()=x
  }
}

class A extends Outter{
  private val i=new A.super.Inner
}

object TypeProject extends App{
  val outter=new Outter
  val inner=new outter.Inner


  val outter2=new Outter
  val inner2=new outter2.Inner
  //下面的这个语句可以成功执行
  outter.print(inner2)
  //注意,下面的这条语句返回的仍然是false,我们只是对print方法中的
  //参数进行类型投影,并没有改变outter.Inner与outter2.Inner
  //是不同类的事实
  println(typeOf[outter.Inner]==typeOf[outter2.Inner])
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30

3. 结构类型

结构类型(Struture Type)通过利用反射机制为静态语言添加动态特性,从面使得参数类型不受限于某个已命名的的类型,例如:

object StructureType {
  //releaseMemory中的方法是一个结构体类型,它定义了
  //一个抽象方法,对close方法的规格进行了说明
  def releaseMemory(res:{def close():Unit}){
    res.close()   
  }

  def main(args: Array[String]): Unit = {
    //结构体使用方式
    releaseMemory(new {def close()=println("closed")})
  }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

另外结构体类型还可以用type关键字进行声明,如:

object StructureType {
  def releaseMemory(res:{def close():Unit}){
    res.close()   
  }
  //采用关键字进行结构体类型声明
  type X={def close():Unit}
  //结构体类型X作为类型参数,定义函数releaseMemory2
  def releaseMemory2(x:X)=x.close()

  def main(args: Array[String]): Unit = {
    releaseMemory(new {def close()=println("closed")})
    //函数使用同releaseMemory
    releaseMemory2(new {def close()=println("closed")})
  }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

从上面的代码来看,结构体类型其实可以看作是一个类,在函数调用时,直接通过new操作来创建一个结构体类型对象,当然它是匿名的。因此,上述方法也可以传入一个实现了close方法的类或单例对象

//定义一个普通的scala类,其中包含了close成员方法
class File{
  def close():Unit=println("File Closed")
}
//定义一个单例对象,其中也包含了close成员方法
object File{
  def close():Unit=println("object File closed")
}

object StructureType {
  def releaseMemory(res:{def close():Unit}){
    res.close()   
  }
  type X={def close():Unit}
  def releaseMemory2(x:X)=x.close()

  def main(args: Array[String]): Unit = {
    releaseMemory(new {def close()=println("closed")})
    releaseMemory2(new {def close()=println("closed")})

    //对于普通的scala类,直接创建对象传入即可使用前述的方法
    releaseMemory(new File())
    //对于单例对象,直接传入单例对象即可
    releaseMemory(File)
  }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26

我们可以看到,虽然说定义的方法中的参数是一个结构体类型,但是我们也可以传入普通类对象和单例对象,只要该对象或类中具有结构体类型中声明的方法即可。上述代码也告诉 我们,其实结构体类型也是一个类,只是表现形式与类有所区别而已。

4. 复合类型

复合类型在前面的课程中其实我们已经有过接触,例如

class B extends A with Cloneable
  • 1

整体 A with Cloneable可以看作是一个复合类型,它也可以通过type关键字来进行声明,例如:


class B extends A with Cloneable

object CompoundType {
  //利用关键字type声明一个复合类型
  type X=A with Cloneable
  def test(x:X)=println("test ok")
  def main(args: Array[String]): Unit = {
    test(new B)
  }
}

猜你喜欢

转载自blog.csdn.net/hellojoy/article/details/81078640