類是對象的藍圖。一旦你定義了類,就能夠用關鍵字new根據類的藍圖建立對象。在類的定義裏,能夠放置字段和方法,這些被籠統地稱爲成員。對於字段,不論是val仍是var定義的,都是指向對象的變量。對於方法,用def定義,包含了可執行代碼。字段保留了對象的狀態或數據,而方法使用這些數據執行對象的運算工做。當類被實例化的時候,運行時環境會預留一些內存來保留對象的狀態映像——即變量的內容。html
建立類示例:java
class SumAccumulator { var sum = 0 }
而後實例化兩次:程序員
val acc: SumAccumulator = new SumAccumulator val csa: SumAccumulator = new SumAccumulator
這時內存裏對象的狀態映像以下:緩存
因爲字段sum是var類型而非val,因此sum能夠被賦予新的Int類型的值:例如安全
acc.sum = 3
此時映像會變成:網絡
圖中同時存在兩個sum變量,一個在acc指向的對象裏,另外一個在csa指向的對象裏。字段的另外一種說法是實例變量(instance variable),由於每個實例都有本身的變量集。多線程
儘管acc和csa都是val類型,可是仍是能夠修改acc指向的對象的值(上面的sum)。val類型對象對acc(或csa)的限制僅限於不能夠把它們再次賦值給其餘對象。例如,下面的嘗試將會失敗:app
//編譯不過,由於acc是val acc = new ChecksumAccumuulator
所以,咱們能夠得出結論,acc將始終指向初始化的Checksuumaccumulator對象,可是對象包含的字段能夠隨時改動。ide
爲了保證對象的健壯性,能夠把類中字段變爲私有的(private)以阻止外界直接對它訪問。由於私有字段只能被定義成在同一類裏的方法訪問,全部跟新字段的代碼將鎖定在類裏。要聲明字段是私有的,能夠把訪問修飾符private放在字段的前面。函數
代碼示例:
class SumAccumulator { private var sum = 0 }
使用private修飾後,任何從類外部對sum的訪問都將失敗
val acc: SumAccumulator = new SumAccumulator acc.sum = 3 //編譯不經過,由於sum是私有的
1. Scala方法參數類型
Scala方法參數中的類型都是val而不是var類型
def add(a: Int, b: Int): Int = { a = a + b //編譯錯誤:Reassignment to val (就是對val類型賦值的錯誤) a }
2. Scala方法中return
Scala方法中的return能夠去掉,從而簡化代碼
代碼示例:
def numSum(num:Int):Int ={ val sum = num + 10 sum //省略return關鍵字,簡化代碼 }
此時返回值就是sum
3. Scala方法中的「=」號
Scala方法中的「=」號很是重要:由於Scala編譯器能夠把任何類型轉換爲Unit,若是定義的函數中」=」號忘記了添加,那麼Scala會默認它返回值爲Unit類型。若你原本想返回一個String類型的值,因爲沒有添加」=」,String類型返回值會被轉換爲Unit類型而丟棄掉。
代碼示例:
def printStr() { "這是String類型的返回值" }
返回結果爲: ()
正確代碼示例:
def printStr(): String = { "這是String類型的返回值" }
返回結果爲: 這是String類型的返回值
4. Scala方法表達式
假如某個方法僅計算單個結果的表達式,這能夠去掉花括號,若是結果表達式很短,甚至能夠把它放在def的同一行裏。
代碼示例:
def numSum(num:Int):Int = num + 10
5. Scala中分號推斷
scala程序中,語句末尾的分號一般是可選的,若一行僅有一個語句也能夠不加分號。不過,若是一行包含多條語句時,分號則是必須的。
不加分號:
if(x < 2) println("too small") else println("ok")
必須加分號:
val x = 10; println(x) //兩個語句,必須加分號
Scala一般的風格是把操做符放在行尾而不是行頭:
錯誤示例:
val x = 10; val y = 3 val z = x //它會被編譯器識別爲z = x ; +y 兩個語句 +y
打印結果:10
正確示例:
val x = 10;
val y = 3
val z = x +
y
println(z)
打印結果:13
Scala分號推斷規則:
6. Scala無參方法
調用Scala類中無參方法時,能夠寫上括號,也能夠不寫。對於類中的取值器來講,去掉()是不錯的風格。
代碼示例:
object exam1 { def main(args: Array[String]): Unit = { val per: Person = new Person per.talk() //ok per.talk //一樣ok } } class Person { def talk(): Unit = println("Talking") }
若是你想強制使用這種風格,能夠在聲明方法時不帶()
代碼示例:
per.talk() //此時這種寫法是錯誤的 per.talk //OK的 class Person { def talk = println("Talking") }
Scala比Java更爲面向對象的特色之一是Scala不能定義靜態成員,而是代之以定義單例對象(singleton Object)。除了用object關鍵字替換了class關鍵字之外,單例對象的定義看上去與類定義一致。
例子:ChecksumAccumulator.scala源碼:
class ChecksumAccumulator { private var sum = 0 def add(b:Byte){sum += b} def checksum():Int = ~(sum & 0xFF) + 1 } import scala.collection.mutable.Map object ChecksumAccumulator { private val cache = Map[String, Int]() def calculate(s:String):Int= if(cache.contains(s)) cache(s) else { val acc = new ChecksumAccumulator for(c <- s) acc.add(c.toByte) val cs = acc.checksum() cache += (s -> cs) cs } }
上面源碼中的單例對象叫作ChecksumAccumulator,與前一個例子裏的類同名。當單例對象與某個類共享同一個名稱時,它就被稱爲是這個類的伴生對象(companion object)。類和它的伴生對象必須定義在一個源文件裏。類被稱爲是這個單例對象的伴生類(companion class)。類和它的伴生對象能夠相互訪問其私有成員。
這段緩存代碼的說明以下:
類和單例對象間的差異是,單例對象不帶參數,而類能夠。由於單例對象不是用new關鍵字實例化的,因此沒有機會傳遞給它實例化參數。每一個單例對象都被實現爲虛擬類(synthetic class)的實例,並指向靜態的變量,由於它們與Java靜態類有着相同的初始化語義。特別要指出的是,單例對象在第一次被訪問的時候纔會被初始化。
不與伴生類共享名稱的單例對象被稱爲獨立對象(standalone object)。它能夠用在不少地方,例如做爲相關功能方法的工具類,或者定義Scala應用的入口點。
想要編寫可以獨立運行的Scala程序,就必須建立有main方法(僅帶一個參數Array[String],且結果類型爲Unit)的單例對象。任何擁有合適簽名的main方法的單例對象均可以用來做爲程序的入口點。
Summer.scala文件源碼:
import ChecksumAccumulator.calculate object Summer { def main(args:Array[String]) { for(arg <- args) println(arg + ": " + calculate(arg)) } }
要執行Summer應用程序,須要把以上的代碼寫入文件Summer.scala中,由於Summer使用了ChecksumAccumulator,因此還要把ChecksumAccumulator的代碼,上面的源碼(類及它的伴生對象),放在文件ChecksumAccumulator.scala中。
Scala和Java之間有一點不一樣,Java須要類名稱與源碼文件名同名,而在Scala對於源文件的命名沒有硬性規定。然而一般狀況下若是不是腳本,推薦的風格是像在Java裏那樣按照所包含的類名來命名文件,這樣程序員就能夠比較容易地根據文件名找到類。
Scala的腳步必須以結果表達式介紹。所以若是你嘗試以腳本方式執行Summer.scala,Scala解釋器將會報錯說Summer.scala不是以結果表達式結束的。正確作法是:須要用Scala編譯器真正的編譯這些文件,而後執行輸出的類文件,方式之一使用Scala的基本編譯器,scalac。
D:\work\workspace\scala>scalac ChecksumAccumulator.scala Summer.scala
D:\work\workspace\scala>
D:\work\workspace\scala>fsc ChecksumAccumulator.scala Summer.scala
D:\work\workspace\scala>
D:\work\workspace\scala>scala Summer of love of: -213 love: -182 D:\work\workspace\scala>
Scala類中使用公有字段的話,任何人均可以修改這個字段,使得安全性大大下降。因此咱們更傾向於使用getter和setter方法:
Scala對類中每一個字段都提供了getter和setter方法,分別叫作age和age_=,
代碼示例:
建立一個exam1.scala文件,文件內容以下
class Person { var age = 0 }
若是想查看這些方法,能夠先編譯Person類,用scalac命令編譯,而後用javap查看字節碼:
D:\work\workspace\scala>scalac exam1.scala D:\work\workspace\scala>javap Person Compiled from "exam1.scala" public class Person { public int age(); public void age_$eq(int); public Person(); } D:\work\workspace\scala>
你能夠本身從新定義getter和setter方法。
代碼示例:
object exam1 { def main(args: Array[String]): Unit = { val per: Person = new Person per.age = 18 per.age = 16 //因爲在setter裏面控制了age不能變小,因此執行結果age不會變 println(per.age) per.age = 19 //使用setter,賦予大於原來的age println(per.age) } } class Person { private var privateAge = 0 //變成私有變量並更名 def age = privateAge def age_=(newAge: Int) { // age_= 不能有空格 if (newAge > privateAge) privateAge = newAge //使得年齡不能變小 } }
打印結果爲:
Scala中每一個字段生成getter和setter的限制:
Scala在實現類中屬性時的四個選擇:
JavaBean規範把Java屬性定義爲一堆getFoo和setFoo方法。相似於Java,當你將Scala字段標註爲 @BeanProperty時,getter和setter方法會自動生成。
代碼示例:
import scala.beans.BeanProperty object exam1 { def main(args: Array[String]): Unit = { val per: Person = new Person per.name = "zhagnsan" per.setName("lisi") //BeanProperty生成的setName方法 println(per.getName) //BeanProperty生成的getName方法 } } class Person { @BeanProperty var name:String = _ }
打印結果爲:
Lisi
上述類Person中由@BeanProperty生成了四個方法:
name: String name_= (newValue: String): Unit getName(): String setName (newValue: String): Unit
圖示爲針對字段生成的方法:
和java或C++同樣,Scala也能夠有任意多的構造器。不過,Scala類有一個構造器比其餘全部構造器都更爲重要,它就是主構造器。除了主構造器外,Scala類還能夠有任意多的輔助構造器。
Scala中輔助構造器和Java或C++十分類似,只有兩處不一樣:
這裏有一個帶有兩個輔助構造器的類。
代碼示例:
object exam1 { def main(args: Array[String]): Unit = { val per1: Person = new Person //主構造器 val per2: Person = new Person("Bob") //第一個輔助構造器 val per3: Person = new Person("Bob",18) //第二個輔助構造器 } } class Person { private var name = "" private var age = 0 //一個輔助構造器 def this(name: String) { this() //調用主構造器 this.name = name } //另外一個輔助構造器 def this(name: String, age: Int) { this(name) //調用前一個輔助構造器 this.age = age } }
在Scala中,每一個類都有主構造器。主構造器並不以this方法定義,而是與類定義交織在一塊兒。
主構造器的參數直接放在類名以後
代碼示例:
object exam1 { def main(args: Array[String]): Unit = { val per: Person = new Person("Bob", 18) //使用主構造器實例化對象 println(per.name + " : " + per.age) } } class Person(val name: String, val age: Int) { //產生私有的name和age,沒有setter //..... }
打印結果:
Bob : 18
上述簡短的Person類定義極大簡化了相同功能的Java代碼:
與上例相同功能的Java代碼示例:
class Person { private String name; private int age; public Person(String name, int age) { this.name = name; this.age = age; } public String name() { return this.name; } public int age() { return this.age; } }
Scala主構造器定義的全部語句都會執行
代碼示例:
class Person(val name: String, val age: Int) { //產生私有的name和age,沒有setter println("主構造器定義的全部語句都會執行") //每次使用主構造器時都會執行 def description = name + "is" + age + "years old" }
不一樣類型的主構造器參數對應會生成的字段和方法:
若是想讓主構造器變成私有的,能夠像這樣放置private關鍵字:
class Person private (val name: String, val age: Int) { //...... }
這樣一來,類用戶就必須經過輔助構造器來構造Person對象了.
在Scala中,你幾乎能夠在任何語法結構中內嵌任何語法構造。你能夠在函數中定義函數,在類中定義類。
代碼示例:
class Network { //在Network類中定義類Member class Member(val name: String) { val contacts = new ArrayBuffer[Member] } private val members = new ArrayBuffer[Member] def join(name: String) = { val m = new Member(name) members += m m } }
考慮有以下兩個網絡:
al chatter = new Network val myFace = new Network
在Scala中,每一個實例都有它本身的Member類(內部類),就和它們有本身的members(內部類)字段同樣。也就是說chatter.Member和myFace.Member是不一樣的兩個類。
做用:拿網絡示例來講,你能夠在各自的網絡中添加成員,可是不能跨網添加成員。
代碼示例:
val chatter = new Network val myFace = new Network val fred = chatter.join("Fred") val wilma = chatter.join("wilma") fred.contacts += wilma //OK val barney = myFace.join("Barney") //錯誤:不能將一個myFace.Member添加到chatter.Member元素緩衝中 fred.contacts += barney
在嵌套類中,你能夠經過 外部類.this 的方式來訪問外部類的this引用,就像Java那樣。
class Network(val name: String) { //在Network類中定義類Member class Member(val name: String) { //.... def description = name + "inside" + Network.this.name } }
若是你以爲須要,也可使用以下語法創建一個指向該引用的別名:
class Network(val name: String) { outer => //在Network類中定義類Member class Member(val name: String) { //.... def description = name + "inside" + outer.name } }
上述語法使得outer變量指向Network.this。對這個變量,你可使用任何合法的名稱。
轉自:https://blog.csdn.net/u011204847/article/details/51105362