kotlin繼承

繼承語法

類和接口的繼承經過 : 來實現java

/**定義一個基類,必須有open關鍵字 才能被繼承*/
open class Base(val p:Int) {

}

/**類繼承*/
class Derived(p:Int):Base(p){

}

/**接口中的方法 默認是 open的*/
interface Animal{
    fun run()
}

//實現接口 不須要 ()
abstract class BaseAnimal() : Animal{
    var name:String ? = null
    constructor(name:String):this(){
        this.name = name
    }
}

class Dog(name: String) : BaseAnimal(name) {
    override fun run() {
        println("$name run ....")
    }
}

fun main() {
    val dog = Dog("哈士奇")
    dog.run()
}

接口

kotlin 的接口能夠包含抽象方法,以及方法的實現,接口能夠有屬性但必須是抽象的,或者提供訪問器的實現,固然java 8 中的接口也支持這些特性了。算法

  • 接口之間的繼承
interface Animal{
    fun die()
    fun eat(){
        //kotlin中的接口方法容許有函數體
        println("animal eat ... ")
    }
}

interface Bird:Animal{
    fun fly(){
        println("自由飛翔")
    }
    //直接覆蓋Animal接口中的方法
    override fun eat() {
        println("bird eat ... ")
    }
}

class Magpie:Bird{
    override fun die() {
        println("鵲 死了")
    }

    override fun eat() {
        super.eat()
        println("鵲 吃穀物")
    }
}

fun main() {
    val magpie = Magpie()
    magpie.eat()
    magpie.fly()
    magpie.die()
}

bird eat ... 鵲 吃穀物 自由飛翔 鵲 死了ide

  • 接口中的屬性與繼承 在kotlin中,在接口中不只能夠定義和實現方法,也能夠定義屬性,在接口中所聲明的屬性,默認是abstract類型的,所以一個類型若是實現了某個接口,則必需要複寫接口中的屬性。在複寫接口屬性時,必須添加override關鍵字,而且定義在接口中的屬性不能被初始化。
interface Animal {
    var name: String
    fun run()
}

class Magpie(override var name: String) : Animal {
    //複寫接口中的屬性的兩種方式
//    override var name: String = "喜鵲"

    override fun run() {
        println("喜鵲 自由飛翔")
    }
}

fun main() {
    val magpie = Magpie("喜鵲")
    magpie.run()
    println(magpie.name)
}

虛類(抽象類)

在kotlin中定義一個虛類,語法與java類似,在虛類中,能夠聲明類屬性,構造函數,能夠只定義方法頭,也能夠定義個包含方法體的完整方法。函數

abstract class Animal(val name: String) {
    var age: Int = 0
    abstract fun run()
    fun eat(){
        println("animal eat ... ")
    }
}

多重繼承

  • 類與接口的繼承 kotlin中也像java同樣一個類(接口)能夠繼承多個接口,只是類繼承接口的時候必須實現接口方法。和java同樣一個類只能繼承一個類,不能繼承多個類。
interface Animal{
    fun run()
}

interface Mammal{
    fun feed()
}

interface Canine:Mammal,Animal{
    fun bite()
}

open class Bird:Animal,Mammal{
    override fun run() {
       //
    }

    override fun feed() {
        //
    }
}

//類繼承 必須有()
class Eagle : Bird(){
    
}
  • 構造函數繼承 若是父類中有多個構造函數,子類隨便繼承父類的一個構造函數就好
open class Animal(var name:String){
    fun run(){
        println("$name run ...")
    }
}

//顯示定義主構造函數類繼承包含主構造函數的父類
class Mammal(name: String):Animal(name){
    fun feed(){
        println("投喂 $name")
    }
}

class Canine:Animal{
    //經過二級構造函數繼承父類的主構造函數
    constructor(name: String):super(name){
        //do something
    }
}
  • 接口方法的多重繼承 不管在java仍是kotlin中,一個類都沒法同時繼承多個父類,可是若是父類又繼承了另外一個基類,如此即可以造成一個 「繼承樹」,在一棵繼承樹中,一個類能夠有多個基類,只不過這些基類之間自己也有繼承關係。在子類中,能夠經過super關鍵字調用父類中的某個方法。當一個類的多個基類都同時定義了同一個方法時,那麼在子類中經過super關鍵字去調用父類的方法時,會調用哪個父類的同名方法呢?
  1. 類的多繼承:
open class Animal(var name:String){
   open fun run(){
       println("animal run ...")
   }
}

open class Mammal(name: String):Animal(name){
    override fun run() {
        println("mammal run ....")
    }
}
class Panda(name: String):Mammal(name){
    override fun run() {
        super.run()
    }
}
fun main() {
    val panda = Panda("盼盼")
    panda.run()
}

mammal run ....this

  1. 接口的多繼承
interface Animal{ 
    fun run(){
       println("animal run ...")
   }
}

interface Mammal:Animal{
    override fun run() {
        println("mammal run ....")
    }
}
class Panda(val name: String):Mammal,Animal{
    override fun run() {
        //這裏必須使用泛型,標明調用哪個父類的方法
        super<Mammal>.run()
    }
}
  • 繼承的初始化 當子類構造函數被執行的時候,期父類以及繼承體系中的全部父類的構造函數都會被執行,類的繼承,不單單是對接口的繼承,更重要的是對字段屬性的繼承。
open class Base{
    constructor(){
        println("base constructor")
    }
    init {
        println("base init")
    }
}
open class ExtendClass1:Base{
    constructor():super(){
        println("ExtendClass1 constructor")
    }
    init {
        println("ExtendClass1 init")
    }
}


open class ExtendClass2:ExtendClass1{
    constructor():super(){
        println("ExtendClass2 constructor")
    }
    init {
        println("ExtendClass2 init")
    }
}
fun main() {
    val base = ExtendClass2()
}

base init base constructor ExtendClass1 init ExtendClass1 constructor ExtendClass2 init ExtendClass2 constructor設計

子類在實例化的時候,沒有實例化父類,那爲何要調用父類的構造函數呢?主要是爲了對字段屬性進行初始化。code

類型轉換

提出類的繼承機制的初衷,大致上處於如下兩個方面考慮:對象

  • 爲了設計一些特殊的算法和模式
  • 爲了實現一成文種通用的設計思想------面向接口設計

關於類型轉換,有兩條不成文的規則:繼承

  1. 能夠向上轉型,不能向下轉
  2. 編譯期不報錯,運行時報錯

在基於面向接口設計原則開發程序時,一定會發生如下兩種轉換:接口

  1. 在調用接口前,會先實例化子類,將子類實例做爲入參傳遞給接口,這裏發生了隱式轉換——有子類轉換成基類
  2. 在接口內部,則能夠將基類類型的入參變量強制轉換爲子類,並調用子類的方法。

在第二步中,將基類強制轉換爲子類,是正確的——由於基類變量實際上指向的是子類實例對象 在kotlin中提供的強制轉換語法是 as , 判斷一個類型是 is (代替了java 中的 instanceof)

val a:Int = 1
    val b:Long = 2L
    //強制轉換
    val c:Number = b as Number
    //判斷類型
    println( a is Int)
相關文章
相關標籤/搜索