定 義類別,本身就是在進行抽象化,如果一個類別定義時不完整,有些狀態或行為必須留待子類別來具體實現,則它是個抽 象類別(Abstract Class)。例如,在定義銀行帳戶時,你也許想將一些帳戶的共同狀態與行為定義在父類別中:
abstract class Account {
protected var bal: Int
def id: String
def name: String
def withdraw(amount: Int)
protected def balance_=(bal: Int)
def balance = bal
def deposit(amount: Int) {
require(amount > 0)
balance = balance - amount
}
override def toString = "Id:\t\t" + id +
"\nName:\t\t" + name +
"\nBalance:\t" + balance
}
在 上面的類別中,粗體字中的定義都是未完成的,例如bal沒有指定值,在Scala中,宣告參考名稱之後一定要明確指定值,除非它是在抽象類別 中的資料成 員;方法id、name、withdraw()與balance_=()都是未實作的,定義中僅表示,這個類別中將會有這些行為(方法)可以 操作,例如在 deposit()中,就操作了balance_=()方法。
由於上面的Account定義是未完成的,因此在類別宣告上加上abstract關 鍵字,表示這是個抽象類別(如果你熟悉Java,會注意到與Java不同的地方是,未實作的方法並不用加上abstract關鍵字)。
你可以繼承Account類別並實作抽象方法以及具體定義抽象資料成員。例如:
class SavingAccount(val id: String, val name: String) extends Account {
protected var bal = 0
protected def balance_=(bal: Int) {
this.bal = bal
}
def withdraw(amount: Int) = {
require(amount > 0)
if(amount <= bal) {
bal -= amount
}
else {
throw new RuntimeException("餘額不足")
}
}
}
如 果在父類別中是抽象成員,在實作同名方法時,並不用加上override關鍵字,例如上例中的balance_=()、withdraw() 方法實作,由 於方法與資料成員屬於同一個名稱空間,若你願意,也可以將父類別中同名的方法改定義為val定義,如上例中的id與name。
你可以如下使用以上的類別定義:
val acct = new SavingAccount("123-456-789", "Justin Lin")
acct.deposit(5000)
acct.withdraw(1000)
println(acct)
// 多型操作
val account: Account = acct
account.deposit(5000)
account.withdraw(1000)
println(account)
在 上例中,Scala自動推斷出acct為SavingAccount,如果要指定類型,就如同上例中account的宣告,由於 account參考的實例 也「是一種」(is a)Account,因此可透過Account型態的變數account來操作。另一個例子則是:
val acct = new SavingAccount("123-456-789", "Justin Lin")
doSome(acct)
println(acct)
// 多型操作
def doSome(account: Account) {
account.deposit(5000)
account.withdraw(1000)
}