Go中如何繼承

[TOC]
在這里簡單分享一下在Go中如何實現(xiàn)繼承。

1. 簡單的組合

說到繼承我們都知道,在Go中沒有extends關(guān)鍵字,也就意味著Go并沒有原生級別的繼承支持。這也是為什么我在文章開頭用了偽繼承這個詞。本質(zhì)上,Go使用interface實現(xiàn)的功能叫組合,Go是使用組合來實現(xiàn)的繼承,說的更精確一點,是使用組合來代替的繼承,舉個很簡單的例子。

1.1 實現(xiàn)父類

我們用很容易理解的動物-貓來舉例子,廢話不多說,直接看代碼。

type Animal struct {
    Name string
}

func (a *Animal) Eat() {
    fmt.Printf("%v is eating", a.Name)
    fmt.Println()
}

type Cat struct {
    *Animal
}

cat := &Cat{
    Animal: &Animal{
        Name: "cat",
    },
}
cat.Eat() // cat is eating

1.2 代碼分析

首先,我們實現(xiàn)了一個Animal的結(jié)構(gòu)體,代表動物類。并聲明了Name字段,用于描述動物的名字。
然后,實現(xiàn)了一個以Animal為receiver的Eat方法,來描述動物進食的行為。
最后,聲明了一個Cat結(jié)構(gòu)體,組合了Cat字段。再實例化一個貓,調(diào)用Eat方法,可以看到會正常的輸出。
可以看到,Cat結(jié)構(gòu)體本身沒有Name字段,也沒有去實現(xiàn)Eat方法。唯一有的就是組合了Animal父類,至此,我們就證明了已經(jīng)通過組合實現(xiàn)了繼承。

2. 優(yōu)雅的組合

上面的僅僅是為了給還沒有了解過Go組合的人看的。作為一個簡單的例子來理解Go的組合繼承,這是完全沒有問題的 。但如果要運用在真正的開發(fā)中,那還是遠遠不夠的。

舉個例子,我如果是這個抽象類的使用者,我拿到animal類不能一目了然的知道這個類干了什么,有哪些方法可以調(diào)用。以及,沒有統(tǒng)一的初始化方式,這意味著凡是涉及到初始化的地方都會有重復(fù)代碼。如果后期有初始化相關(guān)的修改,那么只有一個一個挨著改。所以接下來,我們對上述的代碼做一些優(yōu)化。

2.1 抽象接口

接口用于描述某個類的行為。例如,我們即將要抽象的動物接口就會描述作為一個動物,具有哪些行為。常識告訴我們,動物可以進食(Eat),可以發(fā)出聲音(bark),可以移動(move)等等。這里有一個很有意思的類比。

// 模擬動物行為的接口
type IAnimal interface {
    Eat() // 描述吃的行為
}

// 動物 所有動物的父類
type Animal struct {
    Name string
}

// 動物去實現(xiàn)IAnimal中描述的吃的接口
func (a *Animal) Eat() {
    fmt.Printf("%v is eating\n", a.Name)
}

// 動物的構(gòu)造函數(shù)
func newAnimal(name string) *Animal {
    return &Animal{
        Name: name,
    }
}

// 貓的結(jié)構(gòu)體 組合了animal
type Cat struct {
    *Animal
}

// 實現(xiàn)貓的構(gòu)造函數(shù) 初始化animal結(jié)構(gòu)體
func newCat(name string) *Cat {
    return &Cat{
        Animal: newAnimal(name),
    }
}

cat := newCat("cat")
cat.Eat() // cat is eating

在Go中其實沒有關(guān)于構(gòu)造函數(shù)的定義。例如我們在Java中可以使用構(gòu)造函數(shù)來初始化變量,舉個很簡單的例子,Integer num = new Integer(1)。而在Go中就需要使用者自己通過結(jié)構(gòu)體的初始化來模擬構(gòu)造函數(shù)的實現(xiàn)。

然后在這里我們實現(xiàn)子類Cat,使用組合的方式代替繼承,來調(diào)用Animal中的方法。運行之后我們可以看到,Cat結(jié)構(gòu)體中并沒有Name字段,也沒有實現(xiàn)Eat方法,但是仍然可以正常運行。這證明我們已經(jīng)通過組合的方式了實現(xiàn)了繼承。

2.2 重寫方法

// 貓結(jié)構(gòu)體IAnimal的Eat方法
func (cat *Cat) Eat() {
    fmt.Printf("children %v is eating\n", cat.Name)
}

cat.Eat()
// children cat is eating

可以看到,Cat結(jié)構(gòu)體已經(jīng)重新實現(xiàn)了Animal中的Eat方法,這樣就實現(xiàn)了重寫。

2.3 參數(shù)多態(tài)

什么意思呢?舉個例子,我們要如何在Java中解決函數(shù)的參數(shù)多態(tài)問題?熟悉Java的可能會想到一種解決方案,那就是通配符。用一句話概括,使用了通配符可以使該函數(shù)接收某個類的所有父類型或者某個類的所有子類型。但是我個人認為對于不熟悉Java的人來說,可讀性不是特別友好。

而在Go中,就十分方便了。

func check(animal IAnimal) {
    animal.Eat()
}

在這個函數(shù)中就可以處理所有組合了Animal的單位類型,對應(yīng)到Java中就是上界通配符,即一個可以處理任何特定類型以及是該特定類型的派生類的通配符,再換句人話,啥動物都能處理。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容