定 義類別,本身就是在進行抽象化,如果一個類別定義時不完整,有些狀態或行為必須留待子類別來具體實現,則它是個抽 象類別(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)
}
