Scala學習(六)對象

Scala中對象本質上能夠擁有類的全部特質,甚至能夠擴展其餘類或特質。但有一個例外:你不能提供構造器參數java

1.單例對象

在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的靜態方法實現的。

2.伴生對象

在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文件:

說明:伴生對象能夠被訪問,但並不在做用於當中。舉例來講,類中必須經過 對象名.方法 的方式去訪問,而不是直接調用對象名。

3.擴展類或特質的對象

給個人感受就是Java中的繼承。下面來用代碼演示對象擴展類(對象同樣):

咱們定義了一個抽象的Animal類 和一個 繼承了Animal類的對象Dog,Dog重寫了Animal類中的未定義方法。

上面的演示咱們得知:

  1. Scala中抽象類的定義和Java同樣用abstract關鍵字,繼承和Java同樣用extends關鍵字
  2. 和Java同樣抽象類不能被實例化

4.apply方法

咱們一般會在對象中定義一個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方法,則須要定義在它的伴生對象中。

5.應用程序對象

每一個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")
}

再次編譯效果相同

6.枚舉

和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")

相關文章
相關標籤/搜索