apply() 與 unapply() 方法
如果你可以定義 案例類別(Case class),那麼你可以運用 模式比對 的特性,完成如下的變數指定動作:
定義案例類別的問題之一,在於你必須實際定義出暴露出成員資訊的類別,有時這並不是你想要的,或者你沒辦法定義出這樣的類別。舉個例子來說,字串就是一個例子,如果你有一些學生資料,每筆是 "B123456,Justin,Kaohsiung" 的格式,你希望分別取得 學號、名稱與出生地資訊,基本的作法是:
separate() 函式傳回三個元素的 Tuple,你運用了 Tuple 模式比對 的特性傳回分割後的個別字串。然而,如果你可以這麼作的話,程式看起來會更清楚:
這看起來像是上面 案例類別 的模式比對,問題是字串根本不是案例類別,怎麼可能這麼作?事實上是可行的,你可以定義一個單例物件如下,就可以執行這樣的模式比對功能:
unapply() 方法可以接受你所提供的物件(在這邊是以字串為例,事實上可以是任何類型),經用你所定義的 unapply() 方法內容處理後傳回 Option 物件,事實上,在上例的例子中,編譯器會作如下的處理:
unapply() 方法稱之為 提取方法(Extraction method),而像 Student 這樣只具備提取方法的物件稱之為 提取器(Extractor),提取器讓你對非案例類別的實例,也可以進行模式比對,例如搭配 match 運算式的一個例子如下:
也可以進一步使用模式比對的各種特性,例如使用
模式防護(Pattern guard),找出住在高雄的學生姓名:
相對於
unapply() 方法,apply() 方法則稱之為 注入方法(Injection method),提取方法 與 注入方法 通常同時存在(但非必要),apply() 方法與 unapply() 方法的作用通常是相反的,例如:
- ApplyUnApply.scala
一個使用範例如下:
如果你可以定義 案例類別(Case class),那麼你可以運用 模式比對 的特性,完成如下的變數指定動作:
- case class Apple(price: Int, weight: Int)
- val apple = Apple(10, 20)
- val Apple(p, w) = apple
- println(p) // 10
- println(w) // 20
- def separate(s: String) = {
- val parts = s.split(",")
- if(parts.length == 3) (parts(0), parts(1), parts(2)) else None
- }
- val (number, name, addr) = separate("B123456,Justin,Kaohsiung")
- println(number) // B123456
- println(name) // Justin
- println(addr) // Kaohsiung
- // 這有可能嗎?
- val Student(number, name, addr) = "B123456,Justin,Kaohsiung"
- object Student {
- def unapply(str: String): Option[(String, String, String)] = {
- val parts = str.split(",")
- if (parts.length == 3) Some(parts(0), parts(1), parts(2)) else None
- }
- }
- val Student(number, name, addr) = "B123456,Justin,Kaohsiung"
- println(number) // B123456
- println(name) // Justin
- println(addr) // Kaohsiung
- val Some((number, name, addr)) = Student.unapply("B123456,Justin,Kaohsiung")
- val students = List(
- "B123456,Justin,Kaohsiung",
- "B98765,Monica,Kaohsiung",
- "B246819,Bush,Taipei"
- )
- students.foreach(_ match {
- case Student(nb, name, addr) => println(nb + ", " + name + ", " + addr)
- })
- val students = List(
- "B123456,Justin,Kaohsiung",
- "B98765,Monica,Kaohsiung",
- "B246819,Bush,Taipei"
- )
- students.foreach(_ match {
- case Student(_, name, addr) if addr == "Kaohsiung" => println(name)
- case _ =>
- })
- ApplyUnApply.scala
- object Student {
- def apply(number: String, name: String, addr: String) = {
- number + "," + name + "," + addr
- }
- def unapply(str: String) = {
- val parts = str.split(",")
- if (parts.length == 3) Some(parts(0), parts(1), parts(2)) else None
- }
- }
沒有留言:
張貼留言