從多態角度,理解kotlin的繼承和重寫

昨天剛開始看kotlin,目前過到了類和對象這塊的語法這塊內容,在kotlin成員重寫包括兩種:類屬性重寫和類方法重寫,在類屬性重寫中看到文檔中說val無法重寫var的成員變量,我實在菜鳥教程看的,原文如下:

你可以用一個var屬性重寫一個val屬性,但是反過來不行。因爲val屬性本身定義了getter方法,重寫爲var屬性會在衍生類中額外聲明一個setter方法

不知道你們看完這個是怎麼想的,我看完之後的感覺是:後面解釋和前面的問題不搭邊。明明說的是不能將var屬性重寫爲val,怎麼說的好像是將val屬性重寫爲var屬性出現的問題。。。。。。

之後跟java語言做了一下類比,當時在學java的時候有這麼一個說法:子類的範圍要>=父類的範圍。或者說,從父類繼承的方法,子類的訪問類型只能更大。

最開始學java的時候沒有考慮到這個問題,現在對比着kotlin仔細想一下是可以這麼理解,我舉個簡單的栗子:

class Father{
    
    protected void f(){
        
    }
}

class Child extends Father{
    @Override
    protected void f() {
        super.f();
    }
}

這是再簡單不過的一段代碼,我們稍微改一下:

class Father{
    
    protected void f(){
        
    }
}

class Child extends Father{
    @Override
    public void f() {
        super.f();
    }
}

這個也是沒有問題的,再接着看:

class Father {

    protected void f() {

    }
}

class Child extends Father {
    @Override
    void f() {//報錯了,這裏
        super.f();
    }
}

這裏就會報錯了,因爲之前父類的訪問類型爲protected,第一段代碼的訪問類型是protected,第二個訪問類型是public,都是>=父類的訪問類型。

而第三個默認訪問類型是要比protected訪問類型範圍小,至於private大家可以自己試一下,也是錯誤的。


一開始學的時候只是沒有嘗試過修改複寫方法的訪問類型,只是跟父類的一樣,現在思考一下爲什麼改爲更大的訪問類型,還是正確的?

我是這樣思考的:子類的訪問類型要比父類大,換句話說:使用父類能訪問的,子類都能訪問,可能有的同學這句話聽得有點懵,這個其實就是面向對象的三大特性中的——多態,用父類引用訪問子類對象,敲個代碼就理解了:

public class GFZY {
    public static void main(String[] args) {
        Father child = new Child();
        child.f();
    }
}

class Father {

    protected void f() {

    }
}

class Child extends Father {
    @Override
    public void f() {
        super.f();
    }
}

看到這裏相信大家已經知道我想說什麼了:如果說子類複寫的訪問類型要比父類小,那使用父類引用訪問子類方法的時候就可能會出問題啊!!!!

在上述栗子中,你把子類複寫的方法訪問類型設置爲private或者default的話,使用父類引用的話依舊可以訪問這個方法,但實際上他已經是私有的方法了,這樣不就自相矛盾了嗎。


看完上面這個案例,我們順着這個思路,思考一下kotlin中的實現:

在kotlin中複寫成員屬性時,子類無法將父類的var類型屬性複寫爲val,假如可以這樣複寫,說明在子類中並沒有setXXX的屬性方法。

package kt

open class Student {

    internal open var store: Int = 0

    internal open var s: String = ""

    constructor(num: Int, store: Int) : super() {
        [email protected] = store
        println("構造函數輸出   $num")
    }

}

class HighStudent(var asd: String) : Student(100, 3) {

    override val s: String = ""//這是個錯的,這是當案例用的,,,注意一下

}

那這時候使用父類引用就可以給子類屬性賦值了!!!!!!但是實際上子類已經是val類型的,不能重複賦值。

fun main(args: Array<String>) {

    var highStudent: Student = HighStudent("tom")
    highStudent.s = "我沒法賦值,,,,"

}

而且從var、val的性質來看,var的範圍要比val大,因爲在var要比val多了一個setter函數

本質上還是可以理解爲子類複寫的範圍要>=父類(用多態進行反證)

 

 

 

 

 

 

 

 

 

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章