Scala 魔法函數

jopen 8年前發布 | 32K 次閱讀 Scala Scala開發

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 :_*)

這里 :_* 用來將集合作為可變參數傳遞。

@ Pattern Binders

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

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

 本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!