Scala中對象本質上能夠擁有類的全部特質,甚至能夠擴展其餘類或特質。但有一個例外:你不能提供構造器參數java
在Scala中沒有靜態方法和靜態字段,可是咱們能夠用object語法來達到相同的目的。對象定義了某個類的單個實例:數組
object Util{ private val str = "Hello World"; def out(){ println(str); } }
在解釋器中運行:app
能夠看到,其效果與Java中的靜態方法同樣。this
對象的構造器在該對象第一次被使用時調用。若是一個對象從未被使用,器構造器也不會執行。spa
注:Scala的編譯器能夠編譯txt格式的scala代碼。scala
那麼,爲何object能夠有這樣相似於Java中的靜態的效果呢?咱們來看下Util對象編譯後的class文件:code
咱們將Util對象的代碼進行編譯,以後生成了兩個class文件Util.class 和 Util$.class ,咱們分別查看他們都是什麼:對象
首先Util.class中咱們看到有一個靜態方法out繼承
Util$.class是一個單例模式的類,裏面有咱們Scala的Util對象的str成員,因爲str爲val,因此Util$.class這裏只有str的讀方法str()編譯器
咱們來反編譯Util.class與Util$.class,這樣能夠更加直觀
這裏Util.class的out()方法調用Util$.class的out()方法
注:Scala的對象編譯完成後,會生成對應的Java class。其中方法都是靜態方法,非靜態成員對應到生成的單例類中。
說明:由於Scala對象是單例的這一特性,所以在程序中任何須要使用單例的地方,你均可以用Scala對象實現。
經過上面一系列的分析,咱們清楚了Scala如何經過對象,來實現Java中靜態變量的效果,由於他的底層就是經過java的靜態方法實現的。
在Java或C++中,你一般會用到既有靜態放大又有普通方法的類。在Scala中,你能夠經過類和與類同名的「伴生對象」來達到相同的目的。
伴生對象要求類名和object名稱相同,而且在同一個Scala文件中定義。
咱們來定義一個伴生對象:
class Car{ def stop(){ println("stop..."); } } object Car{ def run(){ println("run...") } }
接下來咱們分別執行類的方法和伴生對象的方法,對比差別。
咱們看到伴生對象中的run()方法能夠不用實例化直接運行(那固然,他是靜態的方法),二類中的stop()方法不能直接運行,須要先實例化才能運行。
接下來咱們看下上面的代碼編譯後是什麼結構。
一樣生成了Car.class與Car$.class,咱們分別用javap命令看下這兩個class的結構
在Car.class中出現了兩個方法,一個是靜態方法run(),也就是咱們在伴生對象中定義的方法。另外一個是普通方法stop(),使咱們在類中定義的方法
Car$.class仍是一個單例,因爲咱們的代碼中沒有定義普通成員,所以這裏很乾淨。
反編譯class文件:
說明:伴生對象能夠被訪問,但並不在做用於當中。舉例來講,類中必須經過 對象名.方法 的方式去訪問,而不是直接調用對象名。
給個人感受就是Java中的繼承。下面來用代碼演示對象擴展類(對象同樣):
咱們定義了一個抽象的Animal類 和一個 繼承了Animal類的對象Dog,Dog重寫了Animal類中的未定義方法。
上面的演示咱們得知:
咱們一般會在對象中定義一個apply方法。當餘姚以下表達式時,apply方法就會被調用:
Object(參數1...參數N)
一般,這樣一個Apply方法返回的是一個伴生對象。
舉例來講,Array數組對象定義了apply方法,讓咱們能夠以這樣的表達式返回一個數組對象
Array(1,2,3,4,5)
爲何不用關鍵字呢?由於對於嵌套表達式而言省去new關鍵字會方便不少,例如:
Array(Array(1,2),Array(2,3))
注意:Array(100)和new Array(100)很容易搞混。前一個表達式時調用了apply方法,返回了一個單元素(整數100)的Array[Int];而第二個表達式調用的是構造器this(100),結果是Array[Nothing],包含100個null元素
apply方法須要定義在對象中,若是類須要定義apply方法,則須要定義在它的伴生對象中。
每一個Scala程序都必須從一個對象的main方法開始,這個方法的類型爲Array[String] => Unit:
object Hello{ def main(args: Array[String]) { println("Hello World") } }
因爲Java中main是靜態的方法,所以Scala中的main方法必須定義在對象中或伴生對象中。
將上面的代碼編譯後執行:
或者直接運行scala文件
Scala中除了使用main方法外,還有另外一種方式實現相同的功能,經過擴展App特質,將代碼寫入構造方法體內:
object Hello extends App{ println("Hello World") }
再次編譯效果相同
和Java或C++不一樣,Scala中並無枚舉類型。不過標準類庫提供了一個Enumeration助手類,能夠用於產出枚舉。
咱們能夠經過下面3種方式構造枚舉:
//統一構建 object MyEnum extends Enumeration{ val Red,Yellow,Green = Value } //與上面同樣,只不過度開寫 object MyEnum extends Enumeration{ val Red = Value; val Yellow = Value; val Green = Value; } //Value方法是一個重載方法,能夠經過它定義枚舉值得id,對應值等 object MyEnum extends Enumeration{ val Red = Value(0,"red");//設置枚舉的id和對應值 val Yellow = Value(10);//設置枚舉的id,值默認與成員同名 val Green = Value("gre");//設置枚舉對應值,id默認前一個枚舉的id+1 }
定義完成後,咱們就能夠用MyEnum.Red、MyEnum.Yellow來引用枚舉值了。
注:枚舉的類型是MyEnum.Value而不是MyEnum,後者是握有這些值的對象。
有人推薦引入一個類型別名,不過這樣須要與import一塊兒用纔會有意義:
object MyEnum extends Enumeration{ type MyEnum = Value val Red,Yellow,Green = Value } import MyEnum._ def doWhat(color:MyEnum) = { if(color == Red) "stop" else "go" }
枚舉值得ID能夠經過方法id返回,名稱經過toString方法返回。
經過values方法返回枚舉值的集合:
for(c <- MyEnum.values) println(c.toString + " " + c.id)
最後你能夠經過ID或者名稱來進行查找定位枚舉值對象:
MyEnum(0) MyEnum.withName("Red")