Scala 魔法函数

jopen 5年前

Scala有一些语法糖,让一些特定名称的函数拥有一些特殊的能力。这些语法糖并没有专门的文档介绍,只是散落在不同的文章和教程中。本文整理里这些魔法函数,并通过例子演示它们的功能。

apply, unapply

当类或对象有一个主要用途的时候, apply 方法为你提供了一个很好的语法糖。比如 a 是一个对象, a.apply(x) 则可以简化为 a(x)
unapply 方法是抽取器(Extractor),有人也翻译成提取器,经常用在模式匹配上。

valfoo = Foo(1)  foo match{  caseFoo(x) => println(x)  }    classFoo(val x: Int){}    objectFoo{  defapply(x: Int) =newFoo(x)  defunapply(f: Foo) = Some(f.x)  }

我们一般都在伴生对象内定义 apply , unapply 方法,但是 Trait , class 内都可以定义这些方法。

update

与 apply 方法类似, update 也是一个魔法方法。对于一个实例 a , Scala可以将 a.update(x,y) 简化为 a(x)=y 。

valbar = Bar(10)  bar(0) =1  println(bar(0))    classBar(n :Int){  vala = Array[Int](n)    defapply(n :Int) = a(n)  defupdate(n:Int, v:Int) = a(n) = v  }    objectBar{  defapply(n :Int) =newBar(n)  }

val bar = Bar(10) 调用伴生对象的 apply 方法生成一个 Bar 的实例。 bar(0) = 1 等价于 bar.update(0,1) 。 println(bar(0)) 等价于 println(bar.apply(0)) ,也就是 class Bar 中定义的 apply 方法。

unapplySeq

类似 unapply ,用来从对象中抽取序列,常用在模式匹配上:

valm = M(1)    m match{  caseM(n1,others@_*) => println(others)  }    classM{}    objectM{  defapply(a: Int*): M =newM  defunapplySeq(m: M): Option[Seq[Int]] = Some(1::2::3:: Nil)  }

identifier_=

如果你只是简单的字段操作,只需定义一个 var 类型的属性即可。但是如果你在set的时候执行额外的逻辑,比如参数检查等,你就可能需要 setter 符号。

Scala为setter提供了特殊的语法:

valobj =newX  obj.x = -1  println(obj.x)  obj.x = 10  println(obj.x)    classX{  var_x =0    //setter  defx_=(n: Int) {  if(n <0) _x =0else_x = n   }    //getter  defx = _x  }

上面的例子中 x_= 就是 x 的setter。

一元操作符

unary_+  unary_-  unary_!  unary_~

如果你想重载/实现一元操作符(+,-,!,~),可以定义上面的方法:

valx =newX('小')  println(!x)  x.s = '黑'  println(!x)    classX(var s: Char){  vals1 ="大小多少长短胖瘦高矮黑白冷暖"    defunary_! = {  vari = s1 indexOf s  if(i %2==0) i = i +1elsei = i -1  newX(s1(i))   }    overridedeftoString ="X("+ s +")"  }

op=

如果你定义了一个操作符 op ,那么 A <op>= B 能自动转换为 A = A <op> B 。

vars =newX("abc")  s ::/ 1  println(s)  s ::/= 1  println(s)    classX(var s: String){  def::/(n:Int) = {  newX(s + n)   }    overridedeftoString ="X("+ s +")"  }

操作符不能是字母和数字(alphanumeric),也不能是!=, ==, <= 或者 >=。

Dynamic

Dynamic对象可以调用不存在的字段或者方法,Scala将它们转换为下面的四个方法:

selectDynamic  updateDynamic  applyDynamic   applyDynamicNamed

这有点动态语言的风格。

例子:

vald =newDynImpl   println(d.foo)   d.foo = 10   println(d.ints(1,2,3))   println(d.ints(i1 = 1, i2 =2,3))    classDynImplextendsDynamic{  varmap = Map.empty[String, Any]    defselectDynamic(name: String) = name    defupdateDynamic(name: String)(value: Any) {   map += name -> value   }    defapplyDynamic(name: String)(args: Any*) = s"method '$name' called with arguments ${args.mkString("'", "', '", "'")}"    defapplyDynamicNamed(name: String)(args: (String, Any)*) = s"method '$name' called with arguments ${args.mkString("'", "', '", "'")}"   }

_*

_* 可以用作模式匹配任意数量的参数:

caseclassA(n: Int*)    vala = A(1,2,3,4,5)  a match{  caseA(1,2, _*) =>  }

你可以将可变参数绑定到一个值上:

a match{  caseA(1,2, as @_*) => println(as)  }

另外 _* 还可以作为辅助类型描述:

vall =1::2::3:: Nil  vala2 = A(l :_*)

这里 :_* 用来将集合作为可变参数传递。

<h2>@ Pattern Binders </h2>

Scala规范8.1.3定义了模式绑定的定义。上面的例子 as @_* 也演示了这个功能。下面再看个例子:

sealedabstractclassExpr  caseclassNumber(num: Double)extendsExpr  caseclassUnOp(operator: String, arg: Expr)extendsExpr    valexpr = UnOp("abs", Number(-1))  expr match{  caseUnOp("abs", e @ Number(_)) => println(e)  case_ =>  }

参考资料

  1. http://stackoverflow.com/questions/1483212/list-of-scalas-magic-functions
  2. http://stackoverflow.com/questions/15799811/how-does-type-dynamic-work-and-how-to-use-it
  3. http://www.scala-lang.org/files/archive/spec/2.11/08-pattern-matching.html#pattern-binders
</div>

来自: http://colobu.com/2016/01/04/Scala-magic-functions/