Scala講座:函數(shù)、操作符及與Java的比較
本文節(jié)選自最近在日本十分流行的Scala講座系列的第三篇,由JavaEye的fineqtbull翻譯。本系列的作者牛尾剛在日本寫過(guò)不少有關(guān)Java和Ruby的書籍,相當(dāng)受歡迎。
包和函數(shù)定義以及類型聲明
不過(guò)還是想把結(jié)婚這個(gè)動(dòng)作明確表現(xiàn)出來(lái)呀,那就試著寫一下吧。對(duì)于函數(shù)式+面向?qū)ο蟮腟cala來(lái)說(shuō)有兩種考慮方式。
***個(gè)是面向?qū)ο蟮姆椒?,?dāng)然就是讓Person類持有表示結(jié)婚的方法getMarriedTo(對(duì)方:Person)了。另一個(gè)就是函數(shù)式方法,結(jié)婚是那女雙方的事情,只在一方的Person類中定義getMarriedTo方法可能也不太確切,可以在Scala特有的單例對(duì)象中(object)定義marry方法來(lái)描述結(jié)婚這一事件。下面的例子中將Person類移到了Life包中,并在Life包中定義了同名的單例對(duì)象(singleton object),然后在Person類和對(duì)象中定義了getMarriedTo和marray方法。另外,因?yàn)檫@里的Person單例對(duì)象與Person類同名
且在同一個(gè)源文件里,所以他們互相又成為伴生對(duì)象和伴生類。
- package life {
- class Person(val firstName:String, val lastName:String, var spouse:Person) {
- def this(fn:String, ln:String) = this(fn, ln, null)
- def introduction = "我的名字是," + firstName + " " + lastName +
- (if (spouse != null) ",對(duì)方的名字是," + spouse.firstName + " " + spouse.lastName + "。" else "。")
- def getMarriedTo(p : Person) {
- this.spouse = p; p.spouse = this //姓可以在以后自由更改
- }
- override def toString : String = super.toString + " [姓: " + lastName + " 名: " + firstName + " 配偶: " +
- (if (spouse != null) " ("+ spouse.lastName + "," + spouse.firstName + ")" else "沒(méi)有") + "]"
- }
- object Person {
- def marry(p1: Person, p2: Person): Unit = {
- p1.spouse = p2; p2.spouse = p1 //姓可以在以后自由更改
- }
- }
- }
上述Unit類型代表不返回任何值,相當(dāng)于Java中的void。如果想把Person類以別的名稱來(lái)使用則可以用import語(yǔ)句來(lái)聲明別名。比如以下程序中為Person類定義了名為Man的別名。
- scala> import life.{Person => Man}
- import life.{Person=>Man}
實(shí)際上述語(yǔ)句與以下聲明type別名的語(yǔ)句是一樣的
- scala> import life.Person
- import life.Person
- scala> type Man = Person
- defined type alias Man
正像這樣,我們可以利用Scala的交互式環(huán)境一邊寫簡(jiǎn)潔的代碼一邊一點(diǎn)一點(diǎn)地確認(rèn)結(jié)果來(lái)進(jìn)行開發(fā)。還有,包和類都可以嵌套定義,這里就省略了。
Scala中操作符也是方法
實(shí)際上Scala并沒(méi)有內(nèi)嵌在語(yǔ)言中的操作符。加法+、乘法*、減法-、除法/、字符串連接+和列表連接++等操作符都是Int、String或List等類型中的方法(有時(shí)可能是父類中的方法)。因此,操作符中的特殊字符在Scala中可以被用作方法名稱的一部分,這對(duì)于定義迷你語(yǔ)言(DSL,特定領(lǐng)域語(yǔ)言)來(lái)說(shuō)是非常重要的。
那么,將“m先生和f女士結(jié)婚后f女士的姓變?yōu)閙”這一動(dòng)作以“m < + f”來(lái)表示吧。在Scala中這
表示“對(duì)接受對(duì)象m適用方法< +,參數(shù)為f”,是“m.< +(f)”的簡(jiǎn)化形式。馬上就在Person類中定義一個(gè)兩元操作符方法“< +”吧。雖然返回值也可以是Unit,這里就以接受對(duì)象自己為返回值吧。
- class Person … {
- …
- def < +(p : Person): Person = { //姓與接受對(duì)象的姓相一致
- this.getMarriedTo(p) //和p結(jié)婚返回值為Unit
- p.lastName = this.lastName //改變姓,賦值表達(dá)式的返回值是Unit
- this //以接受對(duì)象自己作為返回值
- }
- …
- }
下面的代碼是f嫁給了m,f的姓改為了m的姓了。
- scala> import life.Person
- import life.Person
- scala> val m = new Person("Fei", "Zhang")
- m: life.Person = life.Person@14683c0 [姓: Zhang 名: Fei 配偶: 沒(méi)有]
- scala> val f = new Person("Can", "Diao")
- f: life.Person = life.Person@863941 [姓: Diao 名: Can 配偶: 沒(méi)有]
- scala> m < + f
- res0: life.Person = life.Person@14683c0 [姓: Zhang 名: Fei 配偶: (Zhang,Can)]
到這里我們嘗試了一下兩元操作符,Scala也可以定義一元操作符,但不同的是方法名稱的格式為“unary_操作符”。
Java與Scala的混合
Scala可以非常方便的使用Java的類、接口以及其中定義的方法。不僅僅是調(diào)用方法,將Scala類定義為Java類或接口的子類或接口實(shí)現(xiàn)也是很容易的。還有,用scalac編譯Scala類后生成的僅僅是.class文件,完全可以毫無(wú)區(qū)別的把Java和Scala混在一起開發(fā)。
前面定義了life包,現(xiàn)在就定義一個(gè)單例對(duì)象Demo吧,在里面將嵌入使用Java的Swing庫(kù)的例子。將JFrame類在Demo中以Window為別名引入(import),然后就可以看看創(chuàng)建對(duì)象的樣子了。
- object Demo {
- import javax.swing.{JFrame=>Window}
- import javax.swing.JFrame._
- val mameWindow = new Window("window 1")
- mameWindow setSize(200, 150)
- mameWindow setDefaultCloseOperation(EXIT_ON_CLOSE)
- mameWindow setVisible(true)
- }
定義了該單例對(duì)象后,同是與該對(duì)象名同名的Demo類也被定義了??梢杂肈emo來(lái)引用該單例對(duì)象,如下所示執(zhí)行后,可以看到窗口的左上角打開一個(gè)小窗口。
- scala> Demo
- res0: Demo.type = Demo$@1205d8d
Scala與Java在語(yǔ)法上的差異
這里簡(jiǎn)單地列舉一下Scala與Java在語(yǔ)法上的差異。
• 類型的聲明不是“類型 變量 = 值”而是“變量:類型 = 值”。但是,在類型推斷可能的情況下類型聲明可以省略。
• 不可變的變量用val,可變的變量用var來(lái)聲明。任意的數(shù)據(jù)都可以用def來(lái)命名(包括val也可以替換成def)。使用def來(lái)聲明函數(shù)和方法。
• 語(yǔ)句分隔符“;”是可選的,通常用換行來(lái)表示。
• 一連串復(fù)合語(yǔ)句可以用“;”來(lái)分割,然后用“{”和“}”塊來(lái)包括起來(lái)。如果單語(yǔ)句的不用大括號(hào)包括也可以。例如for語(yǔ)句既可以是for(i < - List(1, 2, 3, 4)){println(i)},也可以是for(i < - List(1, 2, 3, 4)) println(i)。
• 包括數(shù)字、字符串和數(shù)組,所有的數(shù)據(jù)都是對(duì)象。包括Java的原類型int、double和bool等所有的數(shù)據(jù)都對(duì)應(yīng)于Scala中的相應(yīng)類。
• void作為Unit類來(lái)處理,Unit的唯一實(shí)例是()。
• 以array(i)來(lái)使用數(shù)組的索引而不是array[I]。數(shù)組項(xiàng)目的取得array(i)和更新array(i) = x也可以認(rèn)為是調(diào)用array.apply(i)和array.update(i, x)方法。
• []可以用來(lái)指定范型的具體類型,比如type IList = List[Int]為聲明項(xiàng)目類型為Int的列表類型??梢杂胊sInstanseOf[T]方法來(lái)強(qiáng)制轉(zhuǎn)換類型,雖然使用了范型之后大部分情況下是用不著的。
• for循環(huán)并不是語(yǔ)法,而是被定義為稱作for-comprehension的語(yǔ)法糖,***被轉(zhuǎn)換成map和filter等方法的組合。
• 有意放棄了靜態(tài)(static)的概念,而是用單例對(duì)象來(lái)取代了靜態(tài)對(duì)象和方法。不是用class而是用object像“object Singleton extends Object { val data: Int }”一樣被定義,其中的屬性可以用來(lái)代替靜態(tài)成員。
• 使用import語(yǔ)句來(lái)引入包和類,并用“_”代替了“*”。比如import javax.swing.JFrame; import javax.swing.JFrame._??梢愿袷?“import javax.swing.{JFrame=>MyWindow}”來(lái)聲明類型的別名。
結(jié)束語(yǔ)
下一講將說(shuō)明以特征(Trait)為形式的mixin式的多重繼承方法、集合、for和map等高階函數(shù)、使用閉包的函數(shù)式編程。
【編輯推薦】