1. 程式人生 > >Scala入門到精通——第二十二節 高級類型 (一)

Scala入門到精通——第二十二節 高級類型 (一)

www 不同 out not cloneabl etag new 創建方式 技術分享

作者:搖擺少年夢
視頻地址:http://www.xuetuwuyou.com/course/12

本節主要內容

  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("搖擺少年夢")) }

當涉及到繼承時,這樣的機制會存在些問題,比如:

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

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

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

}

那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])

}

上述代碼中Outter類中的def print(i:Inner)=i 成員方法中的參數類型Inner事實上相當於def print(i:this.Inner)=idef 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
  }
}

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

(3)在其他類或對象中使用

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

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

明確幾種路徑依賴類型之後,我們能夠對類型投影進行說明:類型投影的目的是將外部類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])
}

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

另外結構體類型還能夠用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")})
  }
}

從上面的代碼來看,結構體類型事實上能夠看作是一個類,在函數調用時,直接通過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)
  }
}

我們能夠看到,盡管說定義的方法中的參數是一個結構體類型,可是我們也能夠傳入普通類對象和單例對象,僅僅要該對象或類中具有結構體類型中聲明的方法就可以。上述代碼也告訴 我們。事實上結構體類型也是一個類。僅僅是表現形式與類有所差別而已。

4. 復合類型

復合類型在前面的課程中事實上我們已經有過接觸。比如

class B extends A with Cloneable

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

加入公眾微信號。能夠了解很多其他最新Spark、Scala相關技術資訊
技術分享

Scala入門到精通——第二十二節 高級類型 (一)