apply() 與 update() 方法


在 介紹 Array 時曾經談過,如果要存取陣列,必須指定陣列索引,與其它語言存取陣列慣例不同的是,在Scala 中,指定陣列索引時是使用()而非[],例如:
val arr = new Array[Int](3)
arr(0) = 10
arr(1) = 20
arr(2) = 30
println(arr(0)) // 顯示 10
println(arr(1)) // 顯示 20
println(arr(2)) // 顯示 30

事實上,上面的程式,Scala會分別將之轉換為update()與apply()方法的呼叫:
val arr = new Array[Int](3)
arr.update(0, 10)
arr.update(1, 20)
arr.update(2, 30)
println(arr.apply(0)) // 顯示 10
println(arr.apply(1)) // 顯示 20
println(arr.apply(2)) // 顯示 30

參 考名稱後直接使用()的寫法,其實是Scala所提供的語法蜜糖,只要遇到(),Scala就會試著將之展開為apply()呼叫,例如 some若是參考 名稱,則呼叫some(.....),則Scala就會試著展開並呼叫some.apply(...),而只要遇到()=呼叫,則Scala 就會試著展開 為update()=呼叫,例如寫下some(...) = value,則Scala會試著展開並呼叫some.update(...) = value。

所以任何類別只要有提供apply()方法或update(),就可以使用()或()=寫法,例如:
class Some {
def apply(a: Int) = a + " from apply...."
def update(a: Int, b: Int) = a + ", " + b + " from update..."
}

val some = new Some()
println(some(1)) // 1 from apply....
println(some(2) = 10) // 2, 10 from update...

基本上,參數也不只能有一個,而參數可以是任何的型態(畢竟只是語法蜜糖),例如:
class Some {
def apply(a: String) = a + " from apply...."
def apply(a: String, b: String) = a + ", " + b + " from apply..."
def update(a: String, b: String) = a + ", " + b + " from update..."
def update(a: String, b: String, c: String) =
a + ", " + b + ", " + c + " from update..."
}

val some = new Some()
println(some("data1")) // data1 from apply....
println(some("data1", "data2")) // data1, data2 from apply...
println(some("data3") = "data4") // data3, data4 from update...
println(some("data3", "data4") = "data5") // data3, data4, date5 from update...

也可以使用 重 複參數,只要記得,如果是()=,等號右邊就是update()最後一個參數:
class Some {
def apply(args: String*) = args.mkString(",")
def update(args: String*) = args.mkString(",")
}

val some = new Some()
println(some("data1")) // data1
println(some("data1", "data2")) // data1,data2
println(some("data3") = "data4") // data3,data4
println(some("data3", "data4") = "data5") // data3,data4,data5

同樣的原則也可以套用在 單 例物件 上,先前在單 例物件也有提過,可以在定義單例物件時提供一個apply方法:
class Resource private {
def service(request: String) = request + "...processed..."
}

object Resource {
private val resource = new Resource
def apply() = resource
}

那麼你就可以這麼使用:
val resource = Resource()
println(resource.service("XD"))

Scala會自動將Resource()的呼叫,轉換為Resource.apply()的呼叫。技 術上,object Resource的語法,Scala會產生一個Resource\$類別,你在 Resource中定義的方法,會是Resource\$類別中的方法之一,而程式中Resource是個參考 名稱,參考至Resource\$的實例。依這個邏輯來看,你可以直接使用Resource()這樣的語法就是很自然的結果,而且就可以知道, 如果願意,你也可以定義update()方法,例如:
object Some {
def apply(args: String*) = args.mkString(",")
def update(args: String*) = args.mkString(",")
}

println(Some("data1")) // data1
println(Some("data1", "data2")) // data1,data2
println(Some("data3") = "data4") // data3,data4
println(Some("data3", "data4") = "data5") // data3,data4,data5