1、前言java
前面學習了Scala的Class,下面接着學習Method(方法)。編程
2、Methodapp
Scala的方法與Java的方法相似,都是添加至類中的行爲,可是在具體的實現細節上差別很大,下面展現一個參數爲整形,返回值爲String的方法定義 ide
// java public String doSomething(int x) { // code here } // scala def doSomething(x: Int): String = { // code here }
Scala中的方法能夠寫的更爲簡潔,以下函數
def plusOne(i: Int) = i + 1
將參數值加1後返回,不用顯示定義返回值類型,除了已經給出的差異,還有其餘差異以下學習
· 指定方法訪問控制(可見性)測試
· 爲方法參數設置默認值的能力ui
· 調用方法時指定方法參數名稱的能力this
· 聲明方法拋出的異常spa
· 在方法中使用var類型的字段
2.1 控制方法的範圍
1. 問題描述
Scala的方法默認爲public的,你須要像Java同樣控制方法的範圍
2. 解決方案
Scala提供了以下範圍控制選項
· 對象私有範圍的
· 私有的
· 受保護的
· 包級別的
· 指定包級別的
· 公有的
最嚴格的訪問就是將方法定義爲對象私有的,其只能在當前對象上被調用,其餘同類型對象沒法訪問,在方法前面添加private[this]便可
private[this] def isFoo = true
在以下示例中,doFoo的參數爲Foo實例,可是isFoo被定義爲對象私有方法,以下代碼編譯不成功
這是由於other沒法訪問isFoo方法,該方法被定義爲只有this(當前對象)能夠訪問
能夠將方法定義爲private,這樣當前對象能夠訪問,而且同類型的其餘對象也能夠訪問
當定義爲private時,子類沒法訪問,以下示例編譯不會經過
將heartBeat定義爲protected以後則可經過編譯
Scala中的protected含義與Java不太相同,其在同一個包的其餘類沒法訪問該protected方法,以下示例
package world { class Animal { protected def breathe {} } class Jungle { val a = new Animal a.breathe // error: this line won't compile } }
編譯時報錯以下
表示Jungle類沒法訪問同包下的Animal類的方法
爲了是方法對於同包下的其餘類可見,可使用private[packageName]來修飾方法
package model { class Foo { private[model] def doX {} private def doY {} } class Bar { val f = new Foo f.doX // compiles f.doY // won't compile } }
運行結果以下
除了讓方法在同包下可見,Scala還可讓包在類繼承結構中的不一樣層級下可見
package com.hust.grid.model { class Foo { private[model] def doX {} private[grid] def doY {} private[hust] def doZ {} } } import com.hust.grid.model._ package com.hust.grid.view { class Bar { val f = new Foo f.doX // won't compile f.doY f.doZ } } package com.hust.common { class Bar { val f = new Foo f.doX // won't compile f.doY // won't compile f.doZ } }
其中doX在model包下其餘類中可見,doY在grid包下其餘類中可見,doZ在hust包下其餘類中可見
若是方法沒有修飾符,其就是public的,下面示例中任何類均可以調用doX方法
package com.hust.grid.model { class Foo { def doX {} } } package org.xyz.bar { class Bar { val f = new com.hust.grid.model.Foo f.doX } }
3. 討論
Scala中的訪問控制符比Java的要稍微複雜點,須要不斷體會和使用
2.2 調用超類的方法
1. 問題描述
爲了保持代碼的簡潔性,你須要調用父類定義的方法
2. 解決方案
在基礎使用上,Scala與Java同樣,使用super指代超類,而後使用方法名指代具體方法
class WelcomeActivity extends Activity { override def onCreate(bundle: Bundle) { super.onCreate(bundle) // more code here ... } }
若是類繼承了多個traits,而且這些traits實現了相同的方法,這時你不只能夠選擇方法,而且還能夠選擇哪一個traits
trait Human { def hello = "the Human trait" } trait Mother extends Human { override def hello = "Mother" } trait Father extends Human { override def hello = "Father" }
你可使用以下不一樣的方法調用hello方法
class Child extends Human with Mother with Father { def printSuper = super.hello def printMother = super[Mother].hello def printFather = super[Father].hello def printHuman = super[Human].hello }
能夠進行以下測試
object Test extends App { val c = new Child println(s"c.printSuper = ${c.printSuper}") println(s"c.printMother = ${c.printMother}") println(s"c.printFather = ${c.printFather}") println(s"c.printHuman = ${c.printHuman}") }
運行結果以下
c.printSuper = Father c.printMother = Mother c.printFather = Father c.printHuman = the Human trait
當類繼承多traits而且有多個相同方法時,可使用super[traitName].methodName 來調用不一樣父類中的相同方法
值得注意的是,其沒法使用到父類的繼承結構(如父類的父類),其只能調用其直接繼承的父類的方法,以下例所示,super[Animal].walk編譯報錯
trait Animal { def walk { println("Animal is walking") } } class FourLeggedAnimal extends Animal { override def walk { println("I'm walking on all fours") } } class Dog extends FourLeggedAnimal { def walkThenRun { super.walk // works super[FourLeggedAnimal].walk // works super[Animal].walk // error: won't compile } }
因爲Dog併爲直接繼承Animal,所以不能使用super[Animal].walk來調用Animal的方法
2.3 設置方法參數的默認值
1. 問題描述
你想要定義方法參數值的默認值,以便在調用方法時不用顯示定義該方法參數
2. 解決方案
在方法簽名中指定默認值,以下所示
class Connection { def makeConnection(timeout: Int = 5000, protocol: = "http") { println("timeout = %d, protocol = %s".format(timeout, protocol)) // more code here } }
timeout的默認值爲5000,protocal的默認值爲"http",能夠經過以下方法調用該方法
c.makeConnection() c.makeConnection(2000) c.makeConnection(3000, "https")
若是你喜歡指定參數名時,可使用以下方式
c.makeConnection(timeout=10000) c.makeConnection(protocol="https") c.makeConnection(timeout=10000, protocol="https")
3. 討論
如同構造函數參數同樣,你能夠爲方法參數設置默認值,方法中參數的賦值從左到右,使用以下方法可使用timeout和protocal的默認值
c.makeConnection()
能夠設置timeout值爲2000,而protocal使用默認值
c.makeConnection(2000)
能夠設置同時設置timeout和protocal的值
c.makeConnection(2000, "https")
可是經過上述方法沒法單獨設置protocal的值,能夠這樣單獨設置protocal的值,而timeout默認爲5000
c.makeConnection(protocol="https")
當你的方法中既有默認值也有非默認值,你須要將由默認值的參數放在最後
能夠上述調用都會報錯,從新調整參數位置便可
2.4 調用方法時使用參數名
1. 問題描述
當調用方法時,你更喜歡使用參數名
2. 解決方案
經常使用的指定參數來調用方法格式以下
methodName(param1=value1, param2=value2, ...)
有以下類
class Pizza { var crustSize = 12 var crustType = "Thin" def update(crustSize: Int, crustType: String) { this.crustSize = crustSize this.crustType = crustType } override def toString = { "A %d inch %s crust pizza.".format(crustSize, crustType) } }
可使用以下方法調用
p.update(crustSize = 16, crustType = "Thick")
p.update(crustType = "Pan", crustSize = 14)
3. 討論
使用參數名調用方法顯得冗長,可是具備更好的可讀性,當參數類型相同時,其顯得尤其有用,可比較以下兩種調用
engage(true, true, true, false)
engage(speedIsSet = true, directionIsSet = true, picardSaidMakeItSo = true, turnedOffParkingBrake = false)
顯然第二種具備更好的可讀性
2.5 定義返回多項(元組)的方法
1. 問題描述
你想要從一個方法中返回多個值,可是你不想將這些值封裝在類中
2. 解決方案
Scala可使用tuple,讓你從方法中返回多個值,以下返回三元組
def getStockInfo = { // other code here ... ("NFLX", 100.00, 101.00) // this is a Tuple3 }
而後調用該方法並將返回值賦值給變量
val (symbol, currentPrice, bidPrice) = getStockInfo
3. 討論
在Java中也能夠從方法中返回多個值,可是其是將多個值封裝在類中而並不是使用tuple
getStockInfo方法返回的是三元組tuple3,tuple最多能夠容納22個變量,即22元組tuple22,以下定義的是二元組
固然也能夠直接使用一個val變量來存儲方法返回值,而後經過._一、._2符號來訪問對應的值
2.6 強制調用者不使用括號調用方法
1. 問題描述
你想強調以下編程風格,在調用訪問方法時不使用括號
2. 解決方案
在定義訪問方法名後不使用括號,以下例所示
class Pizza { // no parentheses after crustSize def crustSize = 12 }
此時,調用者調用方法時不能使用括號,不然會報錯
3. 討論
在調用訪問方法時不適用括號並無任何反作用,而且能夠強制調用者不適用括號進行調用
2.7 建立接受可變參數的方法
1. 問題描述
爲了使方法更爲靈活,你想要定義一個接受可變參數的方法
2. 解決方案
在參數字段類型後面添加*便可表示方法接受可變個參數
def printAll(strings: String*) {
strings.foreach(println)
}
針對以上方法,能夠有以下調用
printAll() printAll("foo") printAll("foo", "bar") printAll("foo", "bar", "baz")
可使用_*操做符來適應序列(Array、List、Seq、Vector等),因此其也可被用做實參調用方法
// a sequence of strings val fruits = List("apple", "banana", "cherry") // pass the sequence to the varargs field printAll(fruits: _*)
3. 討論
當定義一個包含可變參數的方法時,其必需要放在方法簽名的最後,在可變參數後面定義一個參數是非法的
而且一個方法中只能有一個可變參數,當一個參數爲可變參數,調用時你不須要提供任何參數,以下
當定義一個接受可變個整形的參數的方法時,當使用(a)參數調用(b)無參數調用,其結果不相同
當使用一個或多個參數調用時,其是第一個種狀況,當不使用參數調用時,其是第二種狀況,是空列表,這樣能夠防止拋出異常
2.8 定義可拋出異常的方法
1. 問題描述
你想要定義一個能夠拋出異常的方法,這樣能夠警告調用者,或者是你的代碼將會被Java代碼調用
2. 解決方案
使用@throws註釋來聲明可能拋出的異常,將註釋放在方法以前
@throws(classOf[Exception]) override def play { // exception throwing code here ... }
當一個方法可能拋出多個異常時,將其一一列出
@throws(classOf[IOException]) @throws(classOf[LineUnavailableException]) @throws(classOf[UnsupportedAudioFileException]) def playSoundFileWithJavaAudio { // exception throwing code here ... }
3. 討論
Scala使用註釋聲明異常與Java的如下處理方式相同
public void play() throws FooException { // code here ... }
Scala的受檢查異常與Java的不相同,Scala不須要方法聲明會拋出的異常,而且不須要調用者捕捉異常
即使Scala不須要異常時受檢查的,若是不進行測試,也會出現Java中的結果,在下面的代碼中,第二個println永遠不會被調用
object BoomTest extends App { def boom { throw new Exception } println("Before boom") boom // this line is never reached println("After boom") }
2.9 支持流式編程風格
1. 問題描述
你想要建立一個調用可使用流式風格編程的API,其也被稱爲方法鏈
2. 解決方案
流式編程風格可讓你的方法調用聚合在一塊兒,以下所示
person.setFirstName("LEE") .setLastName("SF") .setAge(25) .setCity("WH") .setState("HB")
爲了支持這種風格,作法以下
· 若是類能夠被繼承,可將方法的返回類型定義爲this.type
· 若是類不能被繼承,可將方法的返回類型定義爲this
下面示例展現了this.type做爲set*方法的返回類型
class Person { protected var fname = "" protected var lname = "" def setFirstName(firstName: String): this.type = { fname = firstName this } def setLastName(lastName: String): this.type = { lname = lastName this } } class Employee extends Person { protected var role = "" def setRole(role: String): this.type = { this.role = role this } override def toString = { "%s, %s, %s".format(fname, lname, role) } }
使用以下
object Main extends App { val employee = new Employee // use the fluent methods employee.setFirstName("Al") .setLastName("Alexander") .setRole("Developer") println(employee) }
3. 討論
若是肯定類不會被繼承,則將返回類型定義爲this.type毫無心義,能夠直接返回this便可,以下例所示
final class Pizza { import scala.collection.mutable.ArrayBuffer private val toppings = ArrayBuffer[String]() private var crustSize = 0 private var crustType = "" def addTopping(topping: String) = { toppings += topping this } def setCrustSize(crustSize: Int) = { this.crustSize = crustSize this } def setCrustType(crustType: String) = { this.crustType = crustType this } def print() { println(s"crust size: $crustSize") println(s"crust type: $crustType") println(s"toppings: $toppings") } }
使用以下
object FluentPizzaTest extends App { val p = new Pizza p.setCrustSize(14) .setCrustType("thin") .addTopping("cheese") .addTopping("green olives") .print() }
當類可以被繼承時,則須要將返回類型定義爲this.type,這使得流式風格在子類中也可使用
3、總結
本篇主要講解了Scala中方法的一些使用技巧,謝謝各位園友的觀看~