第一章
- 命令行中使用
:load
命令來加載(編譯并運(yùn)行)文件(腳本文件):scala> :load example.scala
- 編譯為字節(jié)碼文件:
- 腳本:將腳本內(nèi)容封裝入一個(gè)指定的類中晓避,使用
>scalac -Xscript MyClass example.scala
于是會(huì)生成MyClass.class
的文件辈末。 - 有package批糟、類描述的代碼文件:使用
>scalac example.scala
城侧。
- 腳本:將腳本內(nèi)容封裝入一個(gè)指定的類中晓避,使用
- 對(duì)于生成的字節(jié)碼文件蜘腌,假定文件的包在com.example下包颁,使用
>scala -cp . com.example.MyClass
來運(yùn)行宗兼。 - 如果想要java來編譯生成的scala的字節(jié)碼文件氯檐,需要用到
scala-library.jar
文件齐遵,假定該文件在C:\Users\Berlin\.sbt\boot\scala-2.10.6\lib
下寂玲,則寫為>java -cp .;C:\Users\Berlin\.sbt\boot\scala-2.10.6\lib\scala-library.jar MyClass
- val foo定義一個(gè)不可變的量,var foo定義了一個(gè)可變的量梗摇。
- 類的定義:
class Upper { def upper(strings: String*): Seq[String] = { strings.map((s:String) => s.toUpperCase()) } } val up = new Upper println(up.upper("Hello", "World!"))
- 定義了名為Upper的類拓哟,其中有名為upper的方法,
- 方法接受String類型參數(shù)伶授,參數(shù)名為strings断序,個(gè)數(shù)為任意個(gè)(因?yàn)镾tring后有一個(gè)星號(hào)*)。
- 返回類型為Seq泛型類型(序列)糜烹,類型為String违诗,相當(dāng)于Java語法的
Seq<String>
。 - class Upper后沒有參數(shù)列表(比如
class Upper(name:String,age:Int){ def upper... }
)疮蹦,因此構(gòu)造方法沒有參數(shù)诸迟,所以不用寫括號(hào),直接寫val up = new Upper
愕乎。
- 方法定義
def methodName( parameter1 : Type1,parameter2 : Type2 ) : returnedType = { //...method Body }
- 返回類型通痴笪可以省略(遞歸時(shí)要寫)
- 在方法體只有一個(gè)語句時(shí)可以省略花括號(hào)
- 方法體最后一句表達(dá)式的值就是返回值。
- 返回空可以寫成
...) : Unit = {//......}
- 單例對(duì)象
object Upper { def upper(strings: String*) = strings.map(_.toUpperCase()) } println(Upper.upper("Hello", "World!"))
- object關(guān)鍵字定義了一個(gè)名為Upper的單例對(duì)象感论,
- Scala 運(yùn)行時(shí)只會(huì)創(chuàng)建Upper 的一個(gè)實(shí)例绅项。也就是說,你無法通過new 創(chuàng)建Upper 對(duì)象比肄。就好像Java 使用靜態(tài)類型一樣
- 形如
( foo : Type ) => foo.method()
或( foo : Type ) => method(a,foo,b,c)
可以簡(jiǎn)寫為_.method()
或method(a,_,b,c)
- Scala 不支持靜態(tài)類型
- 插值字符串:以s表示:
println( s"Hello ${ this.name } ")
快耿,則變量值會(huì)被替換進(jìn)去湿硝。切記以s為標(biāo)識(shí)。 -
case class:
- 支持模式匹配润努,多用于模式匹配
- 必須有參數(shù)列表,就算沒有也要有括號(hào):
case class Clazz(){....
- 參數(shù)列表中的參數(shù)為public示括,并且是val铺浇,即不可變,可以外部訪問
- case class創(chuàng)建實(shí)例時(shí)可以不用加new(普通class必須加new)
- 默認(rèn)實(shí)現(xiàn)了toString垛膝、hashCode鳍侣、equals方法
- 默認(rèn)是可以序列化的,也就是實(shí)現(xiàn)了Serializable 吼拥;
- 同時(shí)創(chuàng)建了伴生object倚聚,并實(shí)現(xiàn)apply方法
- 更多參考...
- 伴生對(duì)象:case class會(huì)生成與其同名的單例對(duì)象(object),它實(shí)現(xiàn)了
apply
方法凿可。這個(gè)方法是一個(gè)工廠方法惑折,使用case class生成實(shí)例時(shí)不用new,就是因?yàn)閟cala可以自動(dòng)尋找apply方法產(chǎn)生一個(gè)實(shí)例對(duì)象枯跑。因此惨驶,假設(shè)Point是一個(gè)case class,則這兩句話是等價(jià)的:val p1 = Point.apply(1.0, 2.0)
val p2 = Point(1.0, 2.0)
- 可以自己定義伴生對(duì)象敛助。任何時(shí)候只要對(duì)象名(object Clazz {...})和類名(class Clazz {...})相同并且定義在同一個(gè)文件中粗卜,這些對(duì)象就能稱作伴生對(duì)象。
在伴生對(duì)象中可以定義自己的apply纳击,然后使用類名(參數(shù)列表)
即可使用
但是如果這么寫就會(huì)有問題:object Singleton { def apply(age:Int,name:String):Unit = println(s"your name is ${name}, age is ${age}"); } Singleton(name="Amy",age=100); //輸出:your name ....
輸出錯(cuò)誤信息:error: not found: value CaseClass_class CaseClass_{ def apply(s:Int) ={println("good")} } CaseClass_(5)
- equals方法:scala的==會(huì)映射為equals方法续扔,即進(jìn)行值比較(包括對(duì)象)。若比較對(duì)象的內(nèi)存地址焕数,則使用
eq
或ne
:obj1 eq obj2
- 嵌套導(dǎo)入:
object Messages { object Exit // 沒有類體 object Finished case class Response(message: String) } class ShapesDrawingActor { import Messages._ //只在這個(gè)類范圍內(nèi)生效:導(dǎo)入Messages對(duì)象內(nèi)容纱昧,可以直接使用,如Exit堡赔,而不用寫全稱Messages.Exit def ....
- 在Scala 中砌些,main 方法必須為對(duì)象方法(object)。(在Java 中加匈,main 方法必須是類靜態(tài)方法:
object Test{ //而不是 class Test存璃,否則會(huì)提示 CaseClass_.main is not static def main(args: Array[String]) = { //....... } }
第二章
-
變量聲明: val/var name : Type = value
- 不可變:
val array: Array[String] = new Array(5)
- 可變:
var stockPrice: Double = 100.0
變量聲明的同時(shí)必須立即初始化。(例外:如構(gòu)造函數(shù)的參數(shù)列表:class Person(val name: String, var age: Int)
)
- 不可變:
-
Range: 支持Range 的類型包括Int雕拼、Long纵东、Float、Double啥寇、Char偎球、BigInt洒扎、BigDecimal:
- 1 to 10 : [1,10]
- 1 until 10:[1,10)
- 1 to 10 by 3
- 10 to 1 by -3
- 1L to 10L by 3
- 'a' to 'g' by 3
- BigDecimal(1.1) to BigDecimal(10.3) by 3.1
-
偏函數(shù):在偏函數(shù)中只能使用case 語句(處理那些能與至少一個(gè)case 語句匹配的輸入,輸入?yún)s與所有語句都不匹配衰絮,系統(tǒng)就會(huì)拋出一個(gè)MatchError)袍冷,而整個(gè)函數(shù)必須用花括號(hào)包圍。
object CaseClass_{ def main(args: Array[String]) = { var func:PartialFunction[Any,String]={ //輸入任意類型猫牡,返回字符串 case s:String=> //匹配String胡诗,值賦予s "hello "+s case w:Int => //匹配Int,值賦予w "This is Int" case whatAreYouTalking=> //任意類型淌友,賦予變量whatAreYouTalking "Nothing" } println(func("bbc")); println(func(123)); println(func(3.14)); } }
輸出:
hello bbc This is Int Nothing
在偏函數(shù)上調(diào)用isDefinedAt方法可以檢測(cè)某個(gè)實(shí)例是否能被該偏函數(shù)匹配煌恢,返回是布爾值:
func.isDefinedAt(3.14f)
copy 方法:copy 方法也是case 類自動(dòng)創(chuàng)建的。copy 方法允許你在創(chuàng)建case 類的新實(shí)例時(shí)只給出與原對(duì)象不同部分的參數(shù)震庭。例如某case class方法的參數(shù)列表有x瑰抵、y兩個(gè)值且都有默認(rèn)值,則調(diào)用copy方法時(shí)只寫
p.copy(y=3.14)
器联,從而創(chuàng)建一個(gè)新的實(shí)例二汛,它的y是3.14但是x是默認(rèn)值。-
方法具有多個(gè)參數(shù)列表:
-
def draw (offset: Point = Point(0, 0)) (f: String => Unit) = {....}
有兩個(gè)參數(shù)列表 - 使用方法:
draw(Point(1, 2))(str => println("hello")
- 允許把參數(shù)列表兩邊的圓括號(hào)替換為花括號(hào):
draw(Point(1, 2)){str => println("hello")}
- 使用缺省的參數(shù)拨拓,第一個(gè)圓括號(hào)就不能省略:
draw(){str => println("hello")}
- 請(qǐng)注意區(qū)分函數(shù)體和參數(shù)列表习贫,盡管在scala中它們都可用花括號(hào)包裹。
-
-
注意無論是不是多參數(shù)列表千元,只有為單一參數(shù)時(shí)才能大小括號(hào)混用苫昌,否則只能用小括號(hào):
scala> def s(a:Int)(b:String,c:Double)={ | println(a) | println(b,c) | } scala> s{123}{"das",3.14} //因?yàn)榈诙€(gè)列表不是單參數(shù),所以不能用花括號(hào)幸海。 <console>:1: error: ';' expected but ',' found. s{123}{"das",3.14} ^
-
多參數(shù)列表可以進(jìn)行類型推斷:
-
def m1(a: Int, f : Int => String) = .....
祟身,則m1(100, i => s"$i + $i")
會(huì)提示i的類型沒有給定 -
def m2(a: Int)(f: Int => String) = ...
,則m2(100)(i => s"$i + $i")
就米有錯(cuò)物独,Scala可以推斷出i是Int類型袜硫。
-
-
方法的定義還可以嵌套:
def outer() = { def inner() : Int ={ ... } inner(); }
內(nèi)部參數(shù)可以屏蔽同名外部參數(shù)
-
使用
scala.annotation.tailrec
的tailrec注解來檢查遞歸函數(shù)是否實(shí)現(xiàn)了尾遞歸,如果沒有則會(huì)拋出異常:import scala.annotation.tailrec @tailrec def method(...) : Type = { ...}
-
推斷類型信息
- 在java等靜態(tài)語言中挡篓,要寫:
HashMap<Integer, String> intToStringMap = new HashMap<Integer, String>();
或者HashMap<Integer, String> intToStringMap = new HashMap<>();
- 在Scala中只用寫:
val intToStringMap: HashMap[Integer, String] = new HashMap
-
val intToStringMap2 = new HashMap[Integer, String]
非顯式類型注解
- 例如婉陷,
但是如果寫成def report(name:String)={ val copy = name; // copy沒有寫成 var copy:String =name。因?yàn)榭梢宰詣?dòng)推斷出類型 println(s"Your name is ${copy}") } report("Robust") //輸出:Your name is Robust
則會(huì)報(bào)錯(cuò):def report(name:String)={ var copy :String; // 或是 var copy官研,即不指定類型秽澳,但是都沒有初始化 copy = name; println(s"Your name is ${copy}") } report("Robust") //輸出:Your name is Robust
-
var copy;
:error: '=' expected but ';' found. -
var copy :String;
:error: only traits and abstract classes can have declared but undefined members
-
- 在java等靜態(tài)語言中挡篓,要寫:
-
需要顯式類型注解的情況
- 在類中抽象聲明時(shí),聲明了可變的var 變量或不可變的val 變量戏羽,但沒有進(jìn)行初始化担神。
- 所有的方法參數(shù)(如
def deposit(amount: Money) = {…}
)。注意始花,如果寫成def deposit(var amount: Money) = ...
或def deposit(val amount: Money) = ...
就會(huì)報(bào)出兩個(gè)錯(cuò)誤:- error: identifier expected but 'var' found.
-
error: only traits and abstract classes can have declared but undefined members
因此在方法的參數(shù)列表中不要寫val或var
- 方法的返回值類型妄讯,在以下情況中必須顯式聲明其類型:
- 明顯地使用了return
- 遞歸方法
- 兩個(gè)或多個(gè)方法重載(擁有相同的函數(shù)名)孩锡,其中一個(gè)方法調(diào)用了另一個(gè)重載方法,調(diào)用者需要顯式類型注解亥贸。
- Scala 推斷出的類型比你期望的類型更為寬泛躬窜,如Any。
-
_*
的用法:設(shè)def joiner(strings: String*): String = {....}
函數(shù)joiner是一個(gè)接受變長(zhǎng)參數(shù)的函數(shù)炕置,參數(shù)類型為String荣挨。則現(xiàn)在有一個(gè)列表strings: List[String]
,為了將其變?yōu)榉指舻淖冮L(zhǎng)參數(shù)從而可以適應(yīng)joiner的參數(shù)列表讹俊,可以使用:joiner ( strings: _*)
的語法結(jié)構(gòu)。這個(gè)可以這么來理解:- 變量標(biāo)識(shí)符后面的冒號(hào)表示告訴編譯器這個(gè)變量是某種類型
- 下劃線表示類型卻不是指定的煌抒,而是根據(jù)輸入推斷得出的仍劈。Scala 不允許你寫成
strings :String *
,即使你需要為第一個(gè)joiner 方法指定的輸入類型就是String寡壮。(奇怪) -
*
指示是一個(gè)變長(zhǎng)列表
所以綜上所述贩疙,它就是告訴編譯器這個(gè)參數(shù)需要由列表類型“拆分”成變長(zhǎng)參數(shù)列表。
返回類型推斷:最近公共類型况既。假定某方法不指定返回值这溅,其中有一個(gè)
if-else
結(jié)構(gòu),if中返回List[Int]類型結(jié)果棒仍,而else返回List[String]結(jié)果悲靴,則Scala推斷出的返回值類型就是它們的公共父類型,即List[Any]莫其。-
函數(shù)和過程:在scala中能夠定義函數(shù)癞尚。定義的函數(shù)可以有返回值,也可以沒有返回值乱陡。沒有返回值的叫做過程浇揩,有返回值的叫做函數(shù)。在語法上的區(qū)別是是否有等號(hào):
-
def greeting(name:String){ println(s"Hello ${name}"); 3.14159}
這是一個(gè)過程憨颠,因?yàn)閰?shù)列表和花括號(hào)之間沒有等號(hào)胳徽,所以它返回的是Unit
,盡管它會(huì)返回一個(gè)浮點(diǎn)數(shù)3.14159爽彤。打印它的結(jié)果是不可預(yù)知的养盗,通常情況會(huì)打印一個(gè)()
。例如适篙,println(greeting("David"))
會(huì)輸出Hello David
以及()
爪瓜。 -
def greeting(name:String)={ println(s"Hello ${name}")}
這是一個(gè)函數(shù),盡管類型推斷認(rèn)為它也返回Unit
匙瘪,并且println(greeting("David"))
輸出結(jié)果和上面相同铆铆。
-
break 和continue在Scala 中不存在蝶缀。
若方法中含有某些關(guān)鍵字,則使用單引號(hào)來表示薄货。比如,
java.util.Scanner.match
翁都,而match是Scala關(guān)鍵字,所以要寫java.util.Scanner.`match`