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原始檔中可以有多個類。
以上例項的類定義了兩個變數 x 和 y ,一個方法: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)。類和它的伴生物件可以互相訪問其私有成員。
1234567891011 | class 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’來建立一個例項了。
12345 | class 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(特徵)由兩個方法組成:isEqual 和 isNotEqual。isEqual 方法沒有定義方法的實現,isNotEqual定義了方法的實現。子類繼承特徵可以實現未被實現的方法。所以其實 Scala Trait(特徵)更像 Java 的抽象類。
特徵構造順序
特徵也可以有構造器,由欄位的初始化和其他特徵體中的語句構成。這些語句在任何混入該特徵的物件在構造是都會被執行。
構造器的執行順序:
- 呼叫超類的構造器;
- 特徵構造器在超類構造器之後、類構造器之前執行;
- 特質由左到右被構造;
- 每個特徵當中,父特質先被構造;
- 如果多個特徵共有一個父特質,父特質不會被重複構造
- 所有特徵被構造完畢,子類被構造。
構造器的順序是類的線性化的反向。線性化是描述某個型別的所有超型別的一種技術規格。
ref: