[Day 13] Inheritance (繼承)

在 OOP 裡,class 的繼承也是一個很重要的特色,跟 Java 一樣,Kotlin 只能單一繼承,不能繼承多類別。

重點 - open, final, override

open 和 final 是一對相反意義的關鍵字

  • final - 該 class 不能被繼承
  • open - 該 class 能被繼承
  • 預設 class 和 class 屬性 都是 final 的,不能被繼承
  • override - 只能用在 subclass ,用來覆寫繼承的 function 或 property,被覆寫的 function 或 property 預設都會是 open 的

定義子類別(subclass)

這裡我們延用之前的例子 Wallet

先建立子類別 (MyWallet) 來繼承父類別 (Wallet)

繼承

在 Kotlin 中繼承使用 : 符號

class MyWallet : Wallet {
}

https://ithelp.ithome.com.tw/upload/images/20200922/20129902aN6r3U7uuT.png

會發現, Wallet 是 final 不可以被繼承,因為預設 class 都是 final 的

所以把 Wallet 加上 open

open class Wallet(val id: Long, _balance: BigDecimal, _createTime: LocalDateTime) {

但發現 Wallet 至少需要 id 這個初始 constuctor

https://ithelp.ithome.com.tw/upload/images/20200922/201299023XwqvH9O2C.png

所以修改後會是這樣

class MyWallet : Wallet(123) {
}

覆寫父類別的方法

如果想要覆寫父類別的方法,要用 override 關鍵字,而且父類別的方法也要宣告成 open

https://ithelp.ithome.com.tw/upload/images/20200922/20129902DL4MOKaaE6.png

像這樣

open class Wallet(val id: Long, _balance: BigDecimal, _createTime: LocalDateTime) {

	open fun increaseBalance(amount: BigDecimal) {
	    ....
	}
}

覆寫父類別的屬性

一樣父類別的屬性要宣告 open,這裡還用了 protected 來限制只有類別和子類別可以看見這個屬性。

open class Wallet(val id: Long, _balance: BigDecimal, _createTime: LocalDateTime) {
		.....
    protected open var paymentWay: PaymentWay? = PaymentWay("Apple Pay")
}
class MyWallet : Wallet(123) {
    override var paymentWay: PaymentWay? = PaymentWay("My Apple pay")
		...
}

其他子類別繼承 MyWallet

同理,如果要讓其他子類別繼承 MyWallet,MyWallet class 也要宣告成 open

MyWallet override 的 function 或 property 預設都是 open 的,所以如果不要讓其他 subclass override 的話,可以加上 final

如下面的例子

open class MyWallet : Wallet(123) {
    final override var paymentWay: PaymentWay? = PaymentWay("My Apple pay")
		...
}

這樣其他繼承 MyWallet 的 subclass 就無法覆寫 paymentWay

super

如果要在子類別訪問父類別呢? 就要使用 super 這個關鍵字

constructor 的用法

class MyWallet : Wallet {
    
    constructor(id: Long) : super(id)
}

但注意有用 constructor 就不能這樣用預設的寫法了

https://ithelp.ithome.com.tw/upload/images/20200922/20129902vHlcBDfl52.png

super.屬性 和 super.方法

override fun increaseBalance(amount: BigDecimal) {
    println("MyWallet super.balance ${super.balance}")
    super.increaseBalance(amount)
    println("MyWallet increaseBalance $amount")
}

check class type - is

在 Java 中,我們可以用 instanceOf 去檢查一個 class 屬於什麼 class type

在 Kotlin 中,則是使用 is

val myWallet = MyWallet()
println(myWallet is Wallet) // true
println(wallet is MyWallet) // false

Any

如同在 Java 有個 Object 的 class 是所有 class 的父類別

在 Kotlin 則是 Any 這個 class

所以像下面的例子,anyFun(any: Any) 我們可以傳入任意 type 的參數進去。

fun main() {    
	anyFun(myWallet) //it's MyWallet
}

fun anyFun(any: Any) {
    if (any is MyWallet) {
        println("it's MyWallet")
    } else {
        println("it's other type")
    }
}

但是跟在 Java 一樣,如果無法得知確切的 class type 是無法呼叫真正的 class 下相關的 function 的

https://ithelp.ithome.com.tw/upload/images/20200922/20129902P45pB5u1aU.png

所以這時候要使用 as 做強制轉型

(any as MyWallet).decreaseBalance(BigDecimal(200))

https://ithelp.ithome.com.tw/upload/images/20200922/20129902Bt6jdAvsi3.png

smart cast (智慧轉型)

Kotlin 很聰明,如果像下面的例子,因為一開始我們就有判斷 any 是不是 MyWallet,所以其實在判斷後就可以直接使用了,不用再自己手動做轉型,這就是 smart cast

if (any is MyWallet) { // smart cast
    println("it's MyWallet")
    any.decreaseBalance(BigDecimal(0))
} else {
    println("it's other type")
}

今天就講到這裡啦!謝謝大家,我們明天見!

今日練習的程式在這: 請點我