unapply()方法可以僅提取一個結果,例如:
object Address {
def unapply(str: String): Option[String] = {
val data = str.split(",")
if (data.length == 3) Some(data(2)) else None
}
}
val Address(addr) = "B123456,Justin,Kaohsiung"
println(addr) // Kaohsiung
或是傳回Boolean型態,表示模式比對是否成功,可以運用這個特性,改寫上面找出住在高雄的學生姓名之範例:
object Student {
def unapply(str: String) = {
val data = str.split(",")
if (data.length == 3) Some(data(0), data(1), data(2)) else None
}
}
object Kaohsiung {
def unapply(str: String): Boolean = str == "Kaohsiung"
}
val students = List(
"B123456,Justin,Kaohsiung",
"B98765,Monica,Kaohsiung",
"B246819,Bush,Taipei"
)
students.foreach(_ match {
case Student(_, name, addr @ Kaohsiung()) => println(name)
case _ =>
})
注意上例中,模式比對時Kaohsiung()的括號是必要的,這用以區別要使用Kaohsiung的unapply()方法而不是Kaohsiung物件。這個例子也示範了使用提取器進行模式比對的一個好處,你可以連續提取,上例中,先使用Student提取器提取出name與addr,再進一步使用Kaohsiung()提取器判斷addr模式比對是否成功。
如果事先無法知道提取方法會提取出的元素個數,則無法使用 unapply() 方法 來定義提取的動作,例如,也許使用者會輸入一串文字,當中包括大小寫字母,你想要提取出其中大寫字母,但問題是使用者輸入的文字長度是無法預測的,自然也就無法預測所提取的大寫字母不個數會有多少。
若想要提取的元素個數不定,則可以定義unapplySeq()方法,例如:
object Uppercase {
def unapplySeq(s: String): Option[Seq[Char]] = {
Some(for(c <- s.toArray if c.isUpperCase) yield c)
}
}
val Uppercase(u1 @ _*) = "This is Justin"
u1.foreach(print) // TL
val Uppercase(u2 @ _*) = "'JL' stands for Justin Lin"
u2.foreach(print) // TJJLJL
你在 List 模式 中看到的比對方式,都可以套用在上例的提取器中,例如:
val strs = Array(
"Scala is Java?",
"Java is Scala?",
"Java 7 will include closure!",
"Scala already has closure.")
strs.foreach(_ match {
case Uppercase('J') => println("only 'J' mentioned")
case Uppercase('J', 'S') => println("'JS' mentioned")
case other => println("other: " + other)
})
事實上,List 正是實作了 unapplySeq() 方法,才可以使用 List 模式 中所看到的比對模式。
unapplySeq()方法也可以選擇性的傳回固定個數的元素之後,再提供未定個數的元素部份,例如下例找出使用者名稱為"caterpillar"的郵件,取得其網域切割後的字串陣列:
object Email {
def unapplySeq(s: String): Option[(String, Seq[String])] = {
val parts = s.split("@")
if(parts.length == 2) Some((parts(0), parts(1).split("\\.")))
else None
}
}
val strs = Array(
"caterpillar@gmail.com",
"caterpillar@openhome.cc",
"Justin@openhome.cc")
strs.foreach(_ match {
case Email("caterpillar", domain @ _*) => println(domain)
case _ =>
})