繼承是物件導向程式設計語言的一個重要特徵。 繼承允許將類(或基礎類別或父類別)的特性繼承到新類(或派生類或子類)。
主類稱為超類(或父類別),繼承超類的類稱為子類(或子類)。 子類包含超類的特性以及它自己的特性。
當兩個或多個類具有相同的屬性時,這就引入繼承的概念。 繼承用於提高程式碼可重用性。 派生類只有一個基礎類別,但可以有多個介面,而基礎類別可能有一個或多個派生類。
在Kotlin中,派生類在類頭中使用冒號(:
)操作符來繼承基礎類別(在派生類名或建構函式之後)。如下程式碼所示 -
// 宣告一個基礎類別
open class Base(p: Int){
}
// 宣告一個繼承自 Base 類(也稱為基礎類別)的子類:Derived
class Derived(p: Int) : Base(p){
}
假設有兩個不同的類:Programmer
和Salesman
,它們具有公共屬性name
,age
和salary
以及它們各自的方法:functionalitiesdoProgram()
和fieldWork()
。 繼承的特性允許繼承包含公共特性的(Employee
基礎類別)。
open class Employee(name: String, age: Int, salary: Float) {
// code of employee
}
class Programmer(name: String, age: Int, salary: Float): Employee(name,age,salary) {
// code of programmer
}
class Salesman(name: String, age: Int, salary: Float): Employee(name,age,salary) {
// code of salesman
}
所有Kotlin類都有一個共同的超類Any
,它是沒有明確指定超類時使用的預設超類。
例如,類Example
是隱式繼承Any
類的,即使沒有明確寫上。
class Example
由於Kotlin類預設為final
,因此不能簡單地繼承它們。在類之前使用open
關鍵字來為其它類繼承這個類。
範例:
open class Example{
// I can now be extended!
}
當繼承一個類來派生類時,所有的欄位和函式都是繼承的。 可以在派生類中使用這些欄位和函式。
範例:
open class Base{
val x = 10
}
class Derived: Base() {
fun foo() {
println("x is equal to " + x)
}
}
fun main(args: Array<String>) {
val derived = Derived()
derived.foo()
}
執行上面範例程式碼,得到以下結果 -
x is equal to 10
open class Bird {
fun fly() {
println("flying...")
}
}
class Duck: Bird() {
fun swim() {
println("swimming...")
}
}
fun main(args: Array<String>) {
val duck = Duck()
duck.fly()
duck.swim()
}
執行上面範例程式碼,得到以下結果 -
flying...
swimming...
在這裡,宣告一個類Employee
是超類,Programmer
和Salesman
是它的子類。 子類繼承屬性:name
, age
和 salary
以及包含子類自身的函式,如:doProgram()
和fieldWork()
。
open class Employee(name: String, age: Int, salary: Float) {
init {
println("Name is $name.")
println("Age is $age")
println("Salary is $salary")
}
}
class Programmer(name: String, age: Int, salary: Float):Employee(name,age,salary){
fun doProgram() {
println("programming is my passion.")
}
}
class Salesman(name: String, age: Int, salary: Float):Employee(name,age,salary){
fun fieldWork() {
println("travelling is my hobby.")
}
}
fun main(args: Array<String>){
val obj1 = Programmer("Maxsu", 25, 40000f)
obj1.doProgram()
val obj2 = Salesman("Ajax", 24, 30000f)
obj2.fieldWork()
}
執行上面範例程式碼,得到以下結果 -
Name is Maxsu.
Age is 25
Salary is 40000.0
programming is my passion.
Name is Ajax.
Age is 24
Salary is 30000.0
travelling is my hobby.
如果基礎類別和派生類都具有主建構函式,則引數在基礎類別的主建構函式中初始化。 在上面的繼承範例中,所有類都包含三個引數:name
,age
和salary
,所有這些引數都在基礎類別的主建構函式中初始化。
當基礎類別和派生類在主建構函式中包含不同數量的引數時,基礎類別引數將從派生類物件初始化。
範例程式碼
open class Employee(name: String,salary: Float) {
init {
println("姓名:$name.")
println("薪水:$salary")
}
}
class Programmer(name: String, dept: String, salary: Float):Employee(name,salary){
init {
println("$name 所在部門:$dept ,薪水為:$salary.")
}
fun doProgram() {
println("程式設計我有激情.")
}
}
class Salesman(name: String, dept: String, salary: Float):Employee(name,salary){
init {
println("$name 所在部門:$dept ,薪水為:$salary.")
}
fun fieldWork() {
println("我的愛好是:旅遊.")
}
}
fun main(args: Array<String>){
val obj1 = Programmer("Susen", "技術部", 40000f)
obj1.doProgram()
println()
val obj2 = Salesman("Ajax", "市場部", 30000f)
obj2.fieldWork()
}
執行上面範例程式碼,得到以下結果 -
姓名:Susen.
薪水:40000.0
Susen 所在部門:技術部 ,薪水為:40000.0.
程式設計我有激情.
姓名:Ajax.
薪水:30000.0
Ajax 所在部門:市場部 ,薪水為:30000.0.
我的愛好是:旅遊.
當建立派生類的物件時,它首先呼叫超類並執行基礎類別的init
塊,然後執行它自己的init
塊。
如果派生類不包含任何主建構函式,則需要使用super
關鍵字從派生類呼叫基礎類別輔助建構函式。
範例:
open class Patent {
constructor(name: String, id: Int) {
println("執行超類建構函式: $id , $name ")
}
}
class Child: Patent {
constructor(name: String, id: Int, dept: String): super(name, id) {
print("使用屬性執行子類建構函式:$name, $id, $dept")
}
}
fun main(args: Array<String>) {
val child = Child("Maxsu",10010, "技術部")
}
執行上面範例程式碼,得到以下結果 -
執行超類建構函式: 10010 , Maxsu
使用屬性執行子類建構函式:Maxsu, 10010, 技術部
在上面的範例中,當建立Child
類的物件時,它呼叫其建構函式並使用值:「Maxsu」,「10010」和「技術部」初始化其引數。 同時,Child
類建構函式使用具有name
和id
值的super
關鍵字呼叫超類別建構函式。 由於super
關鍵字的存在,超類建構函式的主體首先執行並返回到Child
類建構函式。
方法覆蓋意味著將super(parent)
類的方法的特定實現提供到子類(子)類中。
換句話說,當子類重新定義或修改其超類的方法為子類時,它被稱為方法覆蓋。 方法覆蓋只能在繼承中實現。
Kotlin方法覆蓋的規則
open
的(非final
)。open class Bird {
open fun fly() {
println("Bird is flying...")
}
}
class Parrot: Bird() {
}
class Duck: Bird() {
}
fun main(args: Array<String>) {
val p = Parrot()
p.fly()
val d = Duck()
d.fly()
}
執行上面範例程式碼,得到以下結果 -
Bird is flying...
Bird is flying...
在上面的例子中,一個沒有覆蓋基礎類別方法的程式出現派生類Parrot
和Duck
類執行相同的操作。 為了克服這個問題,使用方法覆蓋這個概念。
在此範例中,子類Parrot
和Duck
中覆蓋父類別Bird
的方法fly()
。 要覆蓋父類別的方法,必須將要覆蓋的父類別及方法宣告為open
。 同時,在子類中重寫的方法必須以override
關鍵字開頭。
open class Bird {
open fun fly() {
println("Bird is flying...")
}
}
class Parrot: Bird() {
override fun fly() {
println("Parrot is flying...")
}
}
class Duck: Bird() {
override fun fly() {
println("Duck is flying...")
}
}
fun main(args: Array<String>) {
val p = Parrot()
p.fly()
val d = Duck()
d.fly()
}
執行上面範例程式碼,得到以下結果 -
Parrot is flying...
Duck is flying...
超類的屬性也可以在子類中覆蓋,這個實現類似於方法。 在子類Parrot
和Duck
中重寫並修改Bird
類的color
屬性。
open class Bird {
open var color = "黑色"
open fun fly() {
println("Bird is flying...")
}
}
class Parrot: Bird() {
override var color = "綠色"
override fun fly() {
println("Parrot is flying...")
}
}
class Duck: Bird() {
override var color = "花色"
override fun fly() {
println("Duck is flying...")
}
}
fun main(args: Array<String>) {
val p = Parrot()
p.fly()
println(p.color)
val d = Duck()
d.fly()
println(d.color)
}
執行上面範例程式碼,得到以下結果 -
Parrot is flying...
綠色
Duck is flying...
花色
可以在繼承中使用var
屬性覆蓋val
屬性,但反之亦然。
派生類也可以使用super
關鍵字呼叫超類方法和屬性。
例如:
open class Bird {
open var color = "黑色"
open fun fly() {
println("Bird is flying...")
}
}
class Parrot: Bird() {
override var color = "綠色"
override fun fly() {
super.fly()
println("Parrot is flying...")
}
}
fun main(args: Array<String>) {
val p = Parrot()
p.fly()
println(p.color)
}
執行上面範例程式碼,得到以下結果 -
Bird is flying...
Parrot is flying...
綠色
在Kotlin中,派生類在尖括號中使用超型別名稱,即,super <Base>
,當它實現多個類中提供的相同函式名時。
例如,派生類Parrot
擴充套件超類Bird
並實現相同的Duck介面函式fly()
。 要呼叫每個類和介面的特定方法,必須在尖括號中提到超型別名稱為super <Bird>.fly()
和super <Duck>.fly()
。
open class Bird {
open var color = "黑色"
open fun fly() {
println("Bird is flying...")
}
}
interface Duck {
fun fly() {
println("Duck is flying...")
}
}
class Parrot: Bird(),Duck {
override var color = "綠色"
override fun fly() {
super<Bird>.fly()
super<Duck>.fly()
println("Parrot is flying...")
}
}
fun main(args: Array<String>) {
val p = Parrot()
p.fly()
println(p.color)
}
執行上面範例程式碼,得到以下結果 -
Bird is flying...
Duck is flying...
Parrot is flying...
綠色