1. 程式人生 > 實用技巧 >Scala 中 object、class 與 trait 的區別

Scala 中 object、class 與 trait 的區別

Scala 中 object、class 與 trait 的區別

引言

當你剛入門 Scala,肯定會迫不及待想要編寫自己的第一個 Scala 程式。如果你已經在互動模式下敲過 Scala 程式碼,想必你更樂意嘗試在 IDEA 下寫 Scala 程式碼。當你開啟 IDEA,滿心期待的建立自己的第一個 Scala 工程,接著建立一個 Scala 類,如下:

你會發現這裡有好幾種類型,如果你之前學過 Java,你會毫不猶豫選擇第一個 Class 型別,然後快速寫下如下程式碼:

class MyFirstScala {
  def main(args: Array[String]): Unit = {
    prinln("hello scala")
  }
}

當你正打算執行這個 main 方法時,卻發現無法執行,我猜你肯定當時就鬱悶了。

要讓 main 方法執行其實很簡單,只需要將 class 改為 object 即可,如下:

object MyFirstScala {
  def main(args: Array[String]): Unit = {
    prinln("hello scala")
  }
}

這到底是怎麼回事呀?

object

單例物件概念

我們都知道 Scala 與 Java 一樣,是一種面向物件的程式語言;但是 Scala 卻與 Java 有所不同,就是 Scala 具有直接建立物件的能力,該物件無需類即可定義其成員

什麼意思呀?就是不需要定義 class 類,也不需要 new,就能直接建立一個物件。而且建立物件的方式和定義類的方式是一樣的,唯一的區別就是建立物件要使用 object 關鍵字。

通過 object 直接建立的物件,我們稱為單例物件。 為何叫單例物件,因為單例物件是沒有類就可以存在的物件,這樣的物件是獨一無二的,不像通過 class 方式可以 new 無數的物件。

那為何 Scala 中程式入口 main() 方法一定要放在 object 建立的單例物件中執行,而不能像 Java 一樣?

許多面向物件的程式語言包括 Java 都是使用 static 關鍵字修飾 main() 方法,這樣 main() 方法就變成了類靜態方法,這樣在類載入完成後就可以直接呼叫 main() 方法了。

但是,Scala 根本就沒有 static 關鍵字, 也沒有類靜態方法和靜態屬性。這就是為什麼在 Scala 中 main() 方法只能放在 object 定義的單例物件中執行。

單例物件定義
object singleton_objname {	    
    // object code , member functions and fields.     
}

注意:object 不能提供構造器引數,也就是說 object 必須是無參的

單例物件用法
object findsum{
    var a = 56
    var b = 21
    def sum(): Int ={
        return a+b;
    }
}

object Main 
{ 
    def print(){
        printf("The sum is : "+ findsum.sum());
    }
	def main(args: Array[String]) 
	{ 
        print();
	} 
} 

伴生物件和伴生類

如果類和單例物件具有相同的名稱,那麼該類為該物件的同伴類,而該物件為該類的伴生物件

伴生物件與伴生類在 Scala 的面向物件程式設計方法中佔據極其重要的位置,主要用於代替 Java 靜態成員變數與靜態方法,很多 Scala 工具方法都是採用單例物件或伴生物件實現的。

伴生類與伴生物件可以互相訪問彼此的私有屬性或方法,但是必須都在同一原始檔

class companion{  
    var a = 23;
    var b = 78;
    def sum(){
        println("The sum is: "+ (a+b));
    }
}  
 
object companion{  
    def main(args:Array[String]){  
        new companion().sum(); 
    }  
}  

class

Scala 是一種面向物件的程式語言,所以其定義和使用類的方式和 Java 基本相同,不過 Scala 也有一些不同的地方之處

構造器
  • 主構造器

    Scala 中每個類都有且只有一個主構造器,且主構造器會執行類定義中的所有語句

    class Student(val name: String,val age: Int){   // 括號中的就是主構造器的引數
        println("Primary constructor")   // 該語句屬於主構造器的一部分
        ...
    }
    

    只有主構造器的引數才會被編譯成欄位,其初始化的值為構造時傳入的引數。

    可以在主構造器中使用預設引數,可防止輔助構造器使用過多。

    class Student(val name: String = "",val age: Int = 0){   // 主構造器的預設引數
     	... 
    }
    
  • 輔助構造器

    輔助構造器的名稱為 this

    class Student {
        private var name = " "
        private var age = 0
     
        def this(name: String){     //輔助構造器1
            this()                  //呼叫主構造器
            this.name = name
        }
        
        def this(name: String,age: Int){        //輔助構造器2 
            this(name)                          //呼叫前一個輔助構造器
            this.age = age
        }
    }
    

    每個輔助構造器都必須以一個對先前已定義的其他輔助構造器或主構造器的呼叫開始

  • 私有構造器

    想要讓主構造器變成私有構造器,只需要加上 private 關鍵字即可

    class Dog private(val age: Int) {
        ...
    }
    

    這樣做之後,就必須使用輔助構造器來構造 Dog 物件了

屬性(成員變數)

scala 對每個類屬性或成員變數都會提供 getter 和 setter 方法,同時也可以顯示的宣告,但是針對 val 型別,只提供getter 方法,預設情況下,欄位為 public 型別

trait

在 Scala 中 trait(特徵) 相當於 Java 的介面,與介面不同的是它還可以定義屬性和方法的實現,這一點又更像 Java 的抽象類。

一般情況下 Scala 的類只能夠繼承單一父類,但是如果是 trait(特徵) 的話就可以繼承多個,從結果來看就是實現了多重繼承。

trait Person {
  def getInfo(): String
}

class Man(var name: String, var age: Int) extends Person {
  override def getInfo(): String = {
    s"info:name=${name},age=${age}"
  }
}

總結

在 Scala 中,object 用於定義單例物件,class 用於定義類,trait 用於定義介面。至於還有兩個 case class / case object 型別,主要用於支援模式匹配。