在本文中,您將學(xué)習(xí)繼承。更具體地說,什么是繼承以及如何在Kotlin中實現(xiàn)使用 繼承(借助示例)。
繼承是面向?qū)ο缶幊痰年P(guān)鍵功能之一。它允許用戶從現(xiàn)有類(基類)創(chuàng)建一個新類(派生類)。
派生類繼承了基類的所有功能,并且可以擁有自己的其他功能。
在詳細介紹Kotlin繼承之前,建議您閱讀以下兩篇文章:
假設(shè)在您的應(yīng)用程序中需要三個角色-一個數(shù)學(xué)老師(MathTeacher),一個足球運動員(Footballer)和一個商人(Businessman)。
由于所有角色都是人,因此他們可以 走路 和 說話。但是,他們也有一些特殊技能。數(shù)學(xué)老師可以教數(shù)學(xué)(teachMath),足球運動員可以踢足球(playFootball),商人可以經(jīng)營企業(yè)(runBusiness)。
您可以單獨創(chuàng)建三個可以走路,說話和執(zhí)行其特殊技能的類。
在每個類中,您將為每個角色復(fù)制相同的步行和說話代碼。
如果要添加新特性 - eat(吃),則需要為每個角色實現(xiàn)相同的代碼。這很容易導(dǎo)致出錯(復(fù)制時)和重復(fù)代碼。
如果我們有一個具有基本功能的 Person 類,比如說,走,吃,睡,并根據(jù)我們的角色為這些功能添加特殊技能,那就容易多了。這是通過繼承完成的。
使用繼承,現(xiàn)在您不需要為每個類實現(xiàn)相同的walk()、talk()和eat()代碼。 你只需要繼承它們就行了。
因此,對于MathTeacher(派生類),您可以繼承Person(基類)的所有功能,并添加一個新功能 teachingMath()。 同樣,對于Footballer類,您繼承了Person類的所有功能,并添加了新功能 playFootball(),依此類推。
這使您的代碼更簡潔,可理解且可擴展。
重要的是要記住:在處理繼承時,每個派生類都應(yīng)滿足其是否為“基”類的條件。 在上面的示例中,MathTeacher是一個 Person(人),F(xiàn)ootballer 是一個 Person(人)。 您不能認為“商人(Businessman)就是企業(yè)(Business)”。
讓我們嘗試在代碼中實現(xiàn)以上討論:
open class Person(age: Int) { //吃飯、說話、走路的代碼 } class MathTeacher(age: Int): Person(age) { //數(shù)學(xué)教師的其他特點 } class Footballer(age: Int): Person(age) { //足球運動員的其他特點 } class Businessman(age: Int): Person(age) { // 商人的其他特征 }
這里,Person是基類,而 MathTeacher,F(xiàn)ootballer 和 Businessman 類則是從 Person 類派生的。
注意,關(guān)鍵字 open 在基類 Person 之前,這點非常重要。
默認情況下,Kotlin中的類是最終的。 如果您熟悉Java,那么您將知道最終類不能被子類化。 通過在類上使用 注解,編譯器允許您從其派生新類。
open class Person(age: Int, name: String) { init { println("我的名字是 $name.") println("我的年齡是 $age") } } class MathTeacher(age: Int, name: String): Person(age, name) { fun teachMaths() { println("我在小學(xué)教書。") } } class Footballer(age: Int, name: String): Person(age, name) { fun playFootball() { println("我為洛杉磯銀河隊效力。") } } fun main(args: Array<String>) { val t1 = MathTeacher(25, "Jack") t1.teachMaths() println() val f1 = Footballer(29, "Christiano") f1.playFootball() }
運行該程序時,輸出為:
我的名字是 Jack. 我的年齡是 25 我在小學(xué)教書。 我的名字是 Cristiano. 我的年齡是 29 我為洛杉磯銀河隊效力。
這里,從 Person 類派生了兩個 MathTeacher 和 Footballer 類。
Person類的主要構(gòu)造函數(shù)聲明了兩個屬性:age 和 name,并且具有一個初始化程序塊。Person派生類(MathTeacher 和 Footballer)的對象可以訪問基類的初始化程序塊(和成員函數(shù))。
派生類 MathTeacher 和 Footballer 分別有自己的成員函數(shù) teachMaths() 和 playFootball()。這些函數(shù)只能從它們各自類的對象訪問。
當(dāng)創(chuàng)建 MathTeacher 類的對象 t1 時,
val t1 = MathTeacher(25, "Jack")
參數(shù)將傳遞給主構(gòu)造函數(shù)。 在Kotlin中,創(chuàng)建對象時會調(diào)用 init 塊。 由于 MathTeacher 是從Person類派生的,因此它將在基類(Person)中查找初始化程序塊并執(zhí)行它。 如果 MathTeacher 具有 init 塊,則編譯器還將執(zhí)行派生類的init塊。
接下來,使用t1.teachMaths()語句調(diào)用對象t1的teachMaths()函數(shù)。
創(chuàng)建類的對象 f1 時,該程序的工作原理類似。 它執(zhí)行基類的init塊。 然后,使用語句f1.playFootball()調(diào)用 Footballer 類的playFootball()方法。
如果該類具有主要構(gòu)造函數(shù),則必須使用主要構(gòu)造函數(shù)的參數(shù)來初始化基類。在上面的程序中,兩個派生類都有兩個參數(shù) age 和 name,并且這兩個參數(shù)都在基類的主構(gòu)造函數(shù)中初始化。
這是另一個實例:
open class Person(age: Int, name: String) { // some code } class Footballer(age: Int, name: String, club: String): Person(age, name) { init { println("年齡為 $age 的足球運動員 $name,為 $club 效力。") } fun playFootball() { println("我正在踢足球。") } } fun main(args: Array<String>) { val f1 = Footballer(29, "Cristiano", "LA Galaxy") }
在此,派生類的主要構(gòu)造函數(shù)具有3個參數(shù),而基類具有2個參數(shù)。請注意,基類的兩個參數(shù)均已初始化。
如果沒有主構(gòu)造函數(shù),則每個基類都必須初始化基類(使用super關(guān)鍵字),或者委托給另一個執(zhí)行此操作的構(gòu)造函數(shù)。 例如
fun main(args: Array<String>) { val p1 = AuthLog("Bad Password") } open class Log { var data: String = "" var numberOfData = 0 constructor(_data: String) { } constructor(_data: String, _numberOfData: Int) { data = _data numberOfData = _numberOfData println("$data: $numberOfData times") } } class AuthLog: Log { constructor(_data: String): this("From AuthLog -> + $_data", 10) { } constructor(_data: String, _numberOfData: Int): super(_data, _numberOfData) { } }
要了解有關(guān)該程序如何工作的更多信息,請訪問Kotlin 次構(gòu)造函數(shù)。
如果基類和派生類包含具有相同名稱的成員函數(shù)(或?qū)傩裕瑒t可能需要使用 override 關(guān)鍵字覆蓋派生類的成員函數(shù),并對基類的成員函數(shù)使用 open 關(guān)鍵字。
// 空的主構(gòu)造函數(shù) open class Person() { open fun displayAge(age: Int) { println("我的年齡是 $age.") } } class Girl: Person() { override fun displayAge(age: Int) { println("我的虛擬年齡是 ${age - 5}.") } } fun main(args: Array<String>) { val girl = Girl() girl.displayAge(31) }
運行該程序時,輸出為:
的虛擬年齡是 26.
在此,girl.displayAge(31) 調(diào)用派生類 Girl 的 displayAge() 方法。
您可以通過類似的方式覆蓋基類的屬性。
在學(xué)習(xí)以下示例之前,可以訪問 Kotlin的 getter 和 setter 查看工作方式。
//空的主要構(gòu)造函數(shù) open class Person() { open var age: Int = 0 get() = field set(value) { field = value } } class Girl: Person() { override var age: Int = 0 get() = field set(value) { field = value - 5 } } fun main(args: Array<String>) { val girl = Girl() girl.age = 31 println("我的虛擬年齡是 ${girl.age}.") }
運行該程序時,輸出為:
我的虛擬年齡是 26.
正如您看到的,我們分別在派生類和基類中為 age 屬性使用了 override 和 open 關(guān)鍵字。
您可以使用super關(guān)鍵字從派生類中調(diào)用基類的函數(shù)(和訪問屬性)。這是如何做:
open class Person() { open fun displayAge(age: Int) { println("我的實際年齡是 $age.") } } class Girl: Person() { override fun displayAge(age: Int) { //調(diào)用基類的函數(shù) super.displayAge(age) println("我的虛擬年齡是 ${age - 5}.") } } fun main(args: Array<String>) { val girl = Girl() girl.displayAge(31) }
運行該程序時,輸出為:
我的實際年齡是 31. 我的虛擬年齡是 26.