在這邊所謂的抽象成員,指的是在抽象類別或特徵(Trait)所定義的成員,像是使用val、var宣告的資料成員(Field),或是使用def所宣告的方法。在抽象類別或特徵(Trait)中定義抽象資料成員,不需要提供初始值,定義抽象方法時,不需要提供實作,繼承的子類別必須提供資料成員初始值或者是方法實作。例如:
trait Something {
val some: String
def doSome: String
}
class Other extends Something {
val some = "some"
def doSome = "do...some"
}
如果是個無參數方法,在子類別可以將之重新定義為val成員,這在 無參數方法 中曾經介紹過,不過對於抽象方法而言,子類別重新定義為val時,不需加上override修飾。例如:
trait Something {
val some: String
def data: String
}
class Other extends Something {
val some = "some"
val data = "data"
}
如果是個var抽象成員,在子類別中可以這麼定義其初始值:
trait Something {
var some: String
}
class Other extends Something {
var some = "some"
}
在 屬性存取方法 中介紹過,var成員其實可以實作為特殊名稱的存取方法。所以事實上,你定義一個var抽象成員時,相當於定義一組抽象方法,例如上例中的Something,相當於以下:
trait Something {
def some: String
def some_=(s: String)
}
所以,對於一個抽象var成員,你可以如下實作沒有問題,編譯器並不會抱怨:
trait Something {
var some: String
}
class Other extends Something {
private[this] var s = "some"
def some: String = "XD"
def some_=(s: String) { this.s = s }
}
如果你想實作某個抽象類別,但不想要定義出類別名稱,也就是你想要以匿名類別(Anonymous class)的方式對抽象類別實作並實例化,則以下是個例子:
abstract class Something(val s: String) {
def doSome: String
}
val something = new Something("XD") { def doSome = s + "...done" }
println(something.doSome) // XD...done
以下則是個使用特徵的例子:
trait Something {
def doSome: String
}
val something = new Something { def doSome = "XD...done" }
println(something.doSome)
抽象類別可以宣告主要建構式,但特徵不行,這決定了你是否透過主要建構式傳入參數來初始化抽象成員。就前兩個例子而言,結果沒什麼不同,但如果是以下的例子,需注意一下執行的順序:
abstract class Something(val s: String) {
println("主要建構式")
def doSome: String
}
def doIt = {
println("函式執行")
"XD"
}
val something = new Something(doIt) { def doSome = s + "...done" }
println(something.doSome)
在使用匿名類別實作Something並建立實例時,要傳入建構式參數,值是由doIt函式決定,所以doIt函式會先執行,接著是Something主要建構式,也就是會先顯示"函式執行",接著再顯示"主要建構式",最後呼叫something.doSome時,顯示"XD...done"。
如果是以下的例子:
trait Something {
println("主要建構式")
def doSome: String
}
def doIt = {
println("函式執行")
"XD"
}
val something = new Something { def doSome = doIt + "...done" }
println(something.doSome)
這邊一樣以匿名類別實作Something並建構實例,此時會先執行Something主要建構式,也就是先顯示"主要建構式",但在執行something.doSome時,才會執行doIt函式取得其結果,也就是顯示"函式執行",接著與"...done"字串串接後傳回,也就是最後顯示"XD...done"。
或許你會覺得,放在doSome方法中的doIt很顯然地,必須呼叫doSome方法才會被執行不是嘛?是的!但如果是以下這個例子,就不是那麼明顯了:
trait Something {
println("主要建構式")
def doSome: String
}
def doIt = {
println("函式執行")
"XD"
}
val something = new Something { val doSome = doIt + "...done" }
println(something.doSome)
這邊一樣以匿名類別實作Something並建構實例,此時會先執行Something主要建構式,也就是先顯示"主要建構式",接著執行匿名類別主要建構式,此時呼叫doIt以取得結果,所以會顯示"函式執行",再來是something.doSome呼叫,所以最後是顯示"XD...done"。