1. 程式人生 > >Scala實戰專欄 (五) ——— 方法

Scala實戰專欄 (五) ——— 方法

scala-basic 基礎篇

@author 魯偉林
Scala基礎知識介紹,主要包括《Scala程式設計實戰》的基礎章節

GitHub地址: https://github.com/thinkingfioa/scala-in-action
本人部落格地址: http://blog.csdn.net/thinking_fioa/article/details/78265745

第5章 方法

Scala的方法和Java的方法非常類似,都是定義在類上的行為。但是也有以下區別點:

  1. 指定方法的訪問控制(可見性)
  2. 給方法引數指定預設值的能力
  3. 方法呼叫時指定引數名傳參的能力
  4. 如何宣告方法可能丟擲的異常
  5. 可變引數的使用

5.1 控制方法作用域

Scala中的方法預設值為public,按照“最嚴格”到“最開放”的順序,Scala提供以下作用域級別:

  1. private[this] ----- 僅對當前例項可見
  2. private ----- 對當前類的所有例項可見
  3. protected ----- 對當前類及其所有子類的例項可見。該點與Java不同,Java中protected同時對同一個包下所有類可見,而Scala不可以
  4. private[packageName](包內可見性) ------ 對*.packageName包下所有類可見
  5. public(公開方法) ----- 如果方法宣告上沒有訪問修飾符,方法就是公開級別。任何包下任何類都可以訪問

5.2 呼叫父類的方法

為了減少重複程式碼,希望呼叫一個父類或者特質中的方法。通常情況下Scala直接呼叫父類的方法和Java是相同的:用super代表父類。但也存在不同點:當類繼承了多個特質,並且特質實現了相同的方法,需要制定使用的特質。

程式碼:

trait Human {
  def hello : String = "the Human trait"
}

trait Mother extends Human {
  override def hello: String = "Mother"
}

trait Father extends Human {
  override def hello: String = "Father"
}

class Child extends Human with Mother with Father {
  def printSupper : String = super.hello
  def printMother : String = super[Mother].hello
  def printFather : String = super[Father].hello
  def printHuman : String = super[Human].hello

  def print(): Unit = {
    println(s"supper $printSupper")
    println(s"Mother $printMother")
    println(s"Father $printFather")
    println(s"Human $printHuman")
  }
}

object Child {
  def apply() = new Child()
}

object Method5P2 {
  def main(args: Array[String]): Unit = {
    val child : Child = Child()
    child.print()
  }
}

注意

當使用supper[traitName].methodName來指定使用哪個特質上的方法時,目標特質必須被當前類通過extends或者with關鍵字擴張,否則編譯失敗。

5.3 方法引數預設值

希望給方法的引數設定預設值,因此呼叫此方法是可以省略傳參。

程式碼

class Connection {
  def connection(timeout : Int = 5000, protocol : String = "http"): Unit = {
    println("timeout = %d, protocol = %s".format(timeout, protocol))
  }
}

object Method5P3 {
  def main(args: Array[String]): Unit = {
    val connection : Connection = new Connection()
    connection.connection()
  }
}

5.4 使用引數名

偏向於在呼叫方法時指定引數名

程式碼

class Pizza {
  var size = 12
  var price = 200

  def update(currSize : Int, currPrice : Int) : Unit = {
    this.size = currSize
    this.price = currPrice
  }

  def print() : Unit = {
    println(s"size $size, price $price")
  }
}

object Method5P4 {

  def main(args: Array[String]): Unit = {
    var pizza : Pizza = new Pizza()
    pizza.update(currPrice = 15, currSize = 100)
    pizza.print()
  }
}

5.5 定義一個返回多個值(Tuples)的方法

希望從一個方法中返回多個值,在Java中,由於無法返回多值,通常使用一個"臨時包裝類"中返回,Scala中只需要以tuple的形式返回即可。

程式碼

class MoreReturnValue() {

  def fetchMoreReturn() : (String, Int, Int) = {
    ("thinking", 23, 125)
  }
}

object Method5P5 {

  def main(args: Array[String]): Unit = {
    val moreReturnValue : MoreReturnValue = new MoreReturnValue()
    val (name, age, weight) = moreReturnValue.fetchMoreReturn()
    println(s"name is $name, age is $age, weight is $weight")
  }
}

5.6 生成Java型別的getter/setter方法 ----- BeanProperty

使用scala.beans.BeanProperty註解,自動生成和Java類似的getter/setter方法。在JSON轉換時非常有用

程式碼

class Pizza5P6(@BeanProperty var price : Int, @BeanProperty var size:Int) {

}

object Method5P6 {

  def main(args: Array[String]): Unit = {
    val pizza : Pizza5P6 = new Pizza5P6(100, 12)
    println("price "+pizza.getPrice)
    println("size "+pizza.getSize)
  }
}

5.7 建立接受變參的方法

為了讓方法更加靈活,可以將方法引數定義為接受多個引數

  1. 在引數型別後面加上一個*。eg: def printAll(strings : String*)
  2. 使用_*來適配一個序列。 eg : def printlnAll(fruits : _*)。從而使得它可以被當作變參傳給一個方法
  3. 變參必須是方法簽名中的最後一個引數

程式碼

class Method5P7 {
  def printAll(strings : String*): Unit = {
    strings.foreach(println)
  }
}

object Method5P7 {
  def main(args: Array[String]): Unit = {
    val method : Method5P7 = new Method5P7()

    method.printAll("thinking", "fioa", "ppp")
    val fruits = List("apple", "apple2")
    method.printAll(fruits : _*)
  }
}

5.8 方法的異常宣告

給方法增加異常宣告,為了讓呼叫者知道也為了可以從Java程式碼呼叫。使用@throws註解宣告可能丟擲的異常。值得注意的是,Scala中不強制要求方法宣告可能丟擲的受檢異常,也不要求呼叫者捕捉受檢異常。但是如果異常發生,執行緒執行會停止。

程式碼

class Method5P8 () {

  @throws[UnsupportedOperationException]
  @throws[NullPointerException]
  def playSound(): Unit = {

  }
}

5.9 支援鏈式呼叫程式設計風格

鏈式呼叫風格的程式碼能夠把方法呼叫連結起來。如: person.setFirstName("thinking").setAge(23)。為了支援這種風格的程式碼,需要:

  1. 如果類可能會被擴充套件,則把this.type作為鏈式呼叫風格方法的返回值型別。
  2. 如果類不會被擴充套件,則把this從鏈式呼叫方法中返回出來。

5.9.1 類會被擴充套件

如果類可以被擴充套件,把方法的返回值顯式指定為this.type能夠確保鏈式呼叫能在自類中仍能正常工作

程式碼

class Person5P9 {
  protected var fname = ""
  protected var lname = ""

  def setFirstName(firstName : String) : this.type = {
    this.fname = firstName
    this
  }

  def setLastName(lastName : String) : this.type = {
    this.lname = lastName
    this
  }
}

class Employee extends Person5P9 {
  protected var role = ""

  def setRole(role : String) : this.type  = {
    this.role = role
    this
  }
}

5.9.2 類不會擴充套件

如果確定類不會被擴充套件,就沒有必要將setXXX方法的返回值型別指定為this.type,只需在每個鏈式方法的最後返回this即可

程式碼

class Employee {
  protected var role = ""

  def setRole(role : String) = {
    this.role = role
    this
  }
}