1. 程式人生 > >Scala:類,物件和特徵(介面)

Scala:類,物件和特徵(介面)

Scala類和物件

類是物件的抽象,而物件是類的具體例項。類是抽象的,不佔用記憶體,而物件是具體的,佔用儲存空間。類是用於建立物件的藍圖,它是一個定義包括在特定型別的物件中的方法和變數的軟體模板。

類的建立和例項化

classPoint(xc:Int, yc:Int){var x:Int= xc
   var y:Int= yc

   def move(dx:Int, dy:Int){
      x = x + dx
      y = y + dy
      println ("x 的座標點: "+ x);
      println ("y 的座標點: "+ y);}}

Scala中的類不宣告為public,一個Scala原始檔中可以有多個類。

以上例項的類定義了兩個變數 xy ,一個方法:move,方法沒有返回值。

Scala 的類定義可以有引數,稱為類引數,如上面的 xc, yc,類引數在整個類中都可以訪問。

接著我們可以使用 new 來例項化類,並訪問類中的方法和變數:

import java.io._

classPoint(xc:Int, yc:Int){var x:Int= xc
   var y:Int= yc

   def move(dx:Int, dy:Int){
      x = x + dx
      y = y + dy
      println ("x 的座標點: "+ x);
      println 
("y 的座標點: "+ y);}}objectTest{def main(args:Array[String]){ val pt =newPoint(10,20);// 移到一個新的位置 pt.move(10,10);}}

例項的預設引數

def this() = this(x = 1, y = 2)

Scala 繼承

Scala繼承一個基類跟Java很相似, 但我們需要注意一下幾點:

  • 1、重寫一個非抽象方法必須使用override修飾符。
  • 2、只有主建構函式才可以往基類的建構函式裡寫引數。
  • 3、在子類中重寫超類的抽象方法時,你不需要使用override關鍵字。

繼承會繼承父類的所有屬性和方法,Scala 只允許繼承一個父類。

Scala重寫一個非抽象方法,必須用override修飾符。

例項:

classPoint(xc:Int, yc:Int){var x:Int= xc
   var y:Int= yc

   def move(dx:Int, dy:Int){
      x = x + dx
      y = y + dy
      println ("x 的座標點: "+ x);
      println ("y 的座標點: "+ y);}}classLocation(override val xc:Int,override val yc:Int,
   val zc :Int)extendsPoint(xc, yc){var z:Int= zc

   def move(dx:Int, dy:Int, dz:Int){
      x = x + dx
      y = y + dy
      z = z + dz
      println ("x 的座標點 : "+ x);
      println ("y 的座標點 : "+ y);
      println ("z 的座標點 : "+ z);}}

Scala 使用 extends 關鍵字來繼承一個類。例項中 Location 類繼承了 Point 類。Point 稱為父類(基類),Location 稱為子類。

override val xc 為重寫了父類的欄位。

Scala單例物件與伴生物件

Scala的單例物件

Scala沒有 static 這個東西,不能定義靜態成員,而是代之定義單例物件(singleton object)。以object關鍵字定義。
物件定義了某個類的單個例項,包含了你想要的特性:

object Accounts{
    private var lastNumber = 0
    def newUniqueNumber() = { lastNumber += 1; lastNumber}
}

當你在應用程式中需要一個新的唯一賬號時,呼叫Account.newUniqueNumber()即可。
物件的構造器在該物件第一次被使用時呼叫。

在下面幾個場景下可以使用Scala單例物件:

- 作為存放工具函式或常量的地方
- 高效地共享單個不可變例項
- 需要使用單個例項來協調某個服務時

類和單例物件間的差別是,單例物件不帶引數,而類可以。因為單例物件不是用new關鍵字例項化的,所以沒機會傳遞給它例項化引數。每個單例物件都被實現為虛擬類(synthetic class)的例項,並指向靜態的變數,因為它們與Java靜態類有相同的初始化語義。

獨立物件(standalone object)

不與伴生類共享名稱的單例物件稱為獨立物件。它可以用在很多地方,例如作為相關功能方法的工具類,或者定義Scala應用的入口點。

伴生物件(companion object)

當單例物件與某個類共享同一個名稱時,它就被稱為是這個類的伴生物件(companion object)。類和它的伴生物件必須定義在同一個原始檔中。類被稱為是這個單例物件的伴生類(companion class)。類和它的伴生物件可以互相訪問其私有成員

1234567891011class Account { val id = Account.newUniqueNumber() private var balance = 0.0 def deposit(amount: Double){ balance += amount } ...}object Account { //伴生物件 private var lastNumber = 0 def newUniqueNumber() = { lastNumber += 1; lastNumber} }

注意

  • 類的伴生物件可以被訪問,但並不在作用域當中。Account類必須通過Account.newUniqueNumber()來呼叫伴生物件的方法。
  • 在REPL中,要同時定義類和物件,必須用貼上模式。鍵入:paste,然後鍵入或貼上類和物件的定義,最後一Ctrl+D退出貼上模式。

將伴生物件作為工廠使用

我們通常將伴生物件作為工廠使用。
下面是一個簡單的例子,可以不需要使用’new’來建立一個例項了。

12345class Bar(foo: String)object Bar { def apply(foo: String) = new Bar(foo)}

單例物件例項

import java.io._

classPoint(val xc:Int, val yc:Int){var x:Int= xc
   var y:Int= yc
   def move(dx:Int, dy:Int){
      x = x + dx
      y = y + dy
   }}objectTest{def main(args:Array[String]){
      val point =newPoint(10,20)
      printPoint

      def printPoint{
         println ("x 的座標點 : "+ point.x);
         println ("y 的座標點 : "+ point.y);}}}

伴生物件例項

/* 檔名:Marker.scala */// 私有構造方法classMarkerprivate(val color:String){
  println("建立"+this)overridedef toString():String="顏色標記:"+ color
}// 伴生物件,與類共享名字,可以訪問類的私有屬性和方法objectMarker{private val markers:Map[String,Marker]=Map(  //一呼叫這個函式,就會建立3個class Marker
      "red"->newMarker("red"),"blue"->newMarker("blue"),"green"->newMarker("green"))def apply(color:String)={if(markers.contains(color)) markers(color)elsenull}def getMarker(color:String)={if(markers.contains(color)) markers(color)elsenull}def main(args:Array[String]){ 
        println(Marker("red"))       //println會呼叫object Marker的toString函式,伴生物件就直接呼叫class Marker的toString函數了。
        // 單例函式呼叫,省略了.(點)符號  
	println(Marker getMarker "blue")}}

$ scalac Marker.scala 
$ scala Marker建立顏色標記:red
建立顏色標記:blue
建立顏色標記:green
顏色標記:red
顏色標記:blue


Scala Trait(特徵)

Scala Trait(特徵) 相當於 Java 的介面,實際上它比介面還功能強大。

與介面不同的是,它還可以定義屬性和方法的實現。

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

Trait(特徵) 定義的方式與類類似,但它使用的關鍵字是 trait,如下所示:

trait Equal{def isEqual(x:Any):Booleandef isNotEqual(x:Any):Boolean=!isEqual(x)}

以上Trait(特徵)由兩個方法組成:isEqualisNotEqual。isEqual 方法沒有定義方法的實現,isNotEqual定義了方法的實現。子類繼承特徵可以實現未被實現的方法。所以其實 Scala Trait(特徵)更像 Java 的抽象類。

特徵構造順序

特徵也可以有構造器,由欄位的初始化和其他特徵體中的語句構成。這些語句在任何混入該特徵的物件在構造是都會被執行。

構造器的執行順序:

  • 呼叫超類的構造器;
  • 特徵構造器在超類構造器之後、類構造器之前執行;
  • 特質由左到右被構造;
  • 每個特徵當中,父特質先被構造;
  • 如果多個特徵共有一個父特質,父特質不會被重複構造
  • 所有特徵被構造完畢,子類被構造。

構造器的順序是類的線性化的反向。線性化是描述某個型別的所有超型別的一種技術規格。

ref: