目录
提取器是从传递给它的对象中提取出构造该对象的参数。
Scala 提取器是一个带有unapply方法的对象。unapply方法算是apply方法的反向操作:unapply接受一个对象,然后从对象中提取值,提取的值通常是用来构造该对象的值。
e.g.
传入name = Amy, age = 10,用于构造Student类,
在函数式编程中,val stu = Student(Amy, 10) 创建了stu对象。这个过程中自动执行了apply方法。
val Student (uname, uage) = stu // 自动执行unapply方法,这里接受一个stu对象,然后从stu对象中提取到了构造这个对象时用到的数据。
这里可以得到 uname = Amy, age = 10
1、apply方法
回顾一下apply,类似构造函数
定义了一个Foo类,并且在这个类中,有一个伴生对象Foo,里面定义了apply方法。有了这个apply方法以后,我们在调用这个Foo类的时候,就可以用函数的方式来调用:
unapply接受一个对象,从对象中提取出相应的值。unapply方法主要用于模式匹配中。
object Client {
def main(args: Array[String]): Unit = {
val foo = Foo("Hello")
}
}
我们用Foo("Hello")
的方式,就得到了一个Foo类型的对象,这一切就是apply方法的功劳。如果没有apply方法,我们将需要使用new关键字来得到Foo对象。原来是这样调用的:
class Foo(foo: String) {
}
object Foo {
def apply(foo: String) : Foo = {
new Foo(foo)
}
}
2、unapply方法
类似析构函数
unapply方法:根据已经创建好的对象,把它里面的参数提取出来,等于apply的反向操作。
例1
package basic.runoob.advanced
class Money(val value: Double, val country: String) {}
object Money {
def apply(value: Double, country: String) : Money = new Money(value, country)
// 提取器
def unapply(money: Money): Option[(Double, String)] = {
println("自动调用 unapply")
if(money == null) {
None
} else {
Some(money.value, money.country)
}
}
}
object MoneyMain{
def main(args: Array[String]): Unit = {
def testUnapply(): Unit = {
val money = Money(10.1, "RMB") // 自动调用apply
println("money: " + money.value) // money: 10.1
money match {
case Money(num, "RMB") => println("RMB: " + num) // 自动调用 unapply
case _ => println("Not RMB!")
}
}
testUnapply() // RMB: 10.1
}
}
例2
package basic.runoob.advanced
// ..../TestUnapply.scala
class Car(val brand:String, val price:Int){
def info(){
println("Car brand is "+brand+"and price is "+price)
}
}
object Car{
def apply(brand:String, price:Int): Car ={
println("Debug: calling apply...")
new Car(brand, price)
}
def unapply(c:Car):Option[(String, Int)]={ //返回值是一个Option类型
println("Debug: calling unapply...")
Some((c.brand, c.price))
}
}
object TestUnapply{ //定义入口函数
def main(args:Array[String]){
// 根据已经创建好的对象,把它里面的参数提取出来,等于apply的反向操作。
val Car(carbrand, carprice) = Car("BMW", 800000) //从右往左执行,先执行等号右边,自动调用apply,再执行左边,调用unapply方法
//右边实例化传入参数生成对象,左右又提取出来,这个在后面做spark编程时会用到这种方式进行模式匹配和提取
println("brand: " + carbrand + " and carprice:" + carprice)
println("=" * 10)
val car1 = Car("aMW", 80)
car1 match {
// case Money(num, "RMB") => println("RMB: " + num) // 自动调用 unapply
case Car(carbrand, carprice) => println(carprice + " ++++ " + carbrand) // 自动调用 unapply
case _ => println("Not RMB!")
}
}
}
/*
Debug: calling apply...
Debug: calling unapply...
brand: BMW and carprice:800000
==========
Debug: calling apply...
Debug: calling unapply...
80 ++++ aMW
*/
例3
unapply封装if-else
Scala可以用unapply封装每一个If-else,比如这个
object App {
def main(args: Array[String]): Unit = {
val e = "123@456.com"
if(isEmail(e)){
println(getPartOne(e))
println(getPartTwo(e))
}else{
println("no an email")
}
}
def isEmail(input:String):Boolean =
input.split("@").length == 2
def getPartOne(input:String) =
input.split("@").apply(0)
def getPartTwo(input:String) =
input.split("@").apply(1)
}
可以变成这个
object App {
def main(args: Array[String]): Unit = {
"123@456.com" match {
case Email(a,b) =>
println(a)
println(b)
case _ => println("isn't an email")
}
}
}
object Email {
def unapply(arg: String): Option[(String,String)] = {
val temp = arg.split("@")
if(temp.length != 2) None
else Some((temp(0),temp(1)))
}
}
备注:apply和unapply都是作为伴生对象中的方法,调用类时,自动进行的操作。
- 构造器的每个参数都成为val,除非显式被声明为var,但是并不推荐这么做;
- 在伴生对象中提供了apply方法,所以可以不使用new关键字就可构建对象;
- 提供unapply方法使模式匹配可以工作;
参考:
Scala入门到精通——第二十五节 提取器(Extractor)