9 struct 和interface: 結(jié)構(gòu)體和接口都實(shí)現(xiàn)了那些功能

方法賦值給變量稱為方法表達(dá)式

age:=Age(25)
//方法賦值給變量,方法表達(dá)式
sm:=Age.String
//通過變量,要傳一個(gè)接收者進(jìn)行調(diào)用也就是age
sm(age)

方法String 其實(shí)沒有參數(shù)的他去,但是通過方法表達(dá)式賦值給變量sm后,在調(diào)用的時(shí)候倒堕,必須要傳一個(gè)接收者,這樣sm才知道怎么調(diào)用

不管方法是否有參數(shù),通過方法表達(dá)式調(diào)用涯雅,第一個(gè)參數(shù)必須是接收者,然后才是方法自身的參數(shù)
結(jié)構(gòu)體:
結(jié)構(gòu)體定義
結(jié)構(gòu)體是一種聚合類型纫事,里面可以包含任何類型的值勘畔,這些值可就是我們定義結(jié)構(gòu)體的成員,也稱為字段丽惶。在Go語言中炫七,要定義一個(gè)結(jié)構(gòu)體需要使用type+struct關(guān)鍵字組合
在下面的例子中,定義一個(gè)結(jié)構(gòu)體類型钾唬,名稱為person,表示一個(gè)人万哪。這個(gè)person結(jié)構(gòu)體有2個(gè)字段: name 代表這個(gè)人的名稱俐筋,age代表年齡

type person struct {
    name string
    age uint
}

在定義結(jié)構(gòu)體時(shí)娘侍,字段聲明方法和平時(shí)聲明一個(gè)變量是一樣的,都是變量名在前驶冒,類型在后儒士,只不過在結(jié)構(gòu)體中的止,變量名稱為成員名字段名

結(jié)構(gòu)體的成員名字段并不是必須的,也可以一個(gè)字段也沒有着撩,這種結(jié)構(gòu)體稱為空結(jié)構(gòu)體
根據(jù)以上信息冲杀,我們可以總結(jié)出結(jié)構(gòu)體定義的表達(dá)式

type structName struct{
    fieldName typeName
    ....
    ....
}

其中:
type和struct 是go語言的關(guān)鍵字,二者組合就代表要定義一個(gè)新的結(jié)構(gòu)體類型
structName 是結(jié)構(gòu)體類型的名字
fieldName 是結(jié)構(gòu)體的字段名睹酌,而 typeName是對應(yīng)字段的;類型
字段可以是零個(gè)剩檀,一個(gè)或者多個(gè)
結(jié)構(gòu)體也是一種類型
定義好結(jié)構(gòu)體后就可以使用了憋沿,因?yàn)樗且粋€(gè)聚合類型,所以比普通類型可以攜帶更多的數(shù)據(jù)
結(jié)構(gòu)體聲明使用

結(jié)構(gòu)體類型和普通的字符串沪猴,整型一樣辐啄,也可以使用同樣的方式聲明和初始化。
在下面的例子中运嗜,聲明一個(gè)person類型的變量p,因?yàn)闆]有對變量p初始化壶辜,所以默認(rèn)會(huì)使用結(jié)構(gòu)體字段的零值。

 var p person

當(dāng)然在聲明一個(gè)結(jié)構(gòu)體變量的時(shí)候担租,也可以通過結(jié)構(gòu)體字面量的方式初始化砸民,如下面的代碼所示:

p:=person{"飛雪",30}

采用簡短聲明發(fā),同時(shí)采用字面量初始化的方式,把結(jié)構(gòu)體變量p的name 初始化為"飛雪",age初始化為30岭参,以逗號(hào)分隔

聲明一個(gè)結(jié)構(gòu)體變量后就可以使用了反惕,下面運(yùn)行以下代碼,驗(yàn)證name和age的值是否和初始化的一樣:


fmt.printIn(p.name,p.age)

在Go語言中演侯,訪問一個(gè)結(jié)構(gòu)體的字段和調(diào)用一個(gè)類型的方法一樣姿染,都是使用點(diǎn)操作符"."
采用字面量初始化結(jié)構(gòu)體時(shí),初始化值的順序很重要秒际,必須和字段定義的順序一致

在person這個(gè)結(jié)構(gòu)體中悬赏,第一個(gè)字段是string類型的name,第二個(gè)字段是unit類型的age,所以在初始化的時(shí)候,初始化值的類型順序必須一致娄徊,才能編譯通過闽颇,也就是說在示例 {“飛雪無情”,30} 中,表示 name 的字符串飛雪無情必須在前嵌莉,表示年齡的數(shù)字 30 必須在后进萄。
那么是否可以不按照順序初始化呢?當(dāng)然可以锐峭,只不過需要指出子彈名稱中鼠,如下所示:

p:=person{age:30,name:"飛雪無情"}

其中,第一位我放了整型的 age沿癞,也可以編譯通過援雇,因?yàn)椴捎昧嗣鞔_的 field:value 方式進(jìn)行指定,這樣 Go 語言編譯器會(huì)清晰地知道你要初始化哪個(gè)字段的值椎扬。
有沒有發(fā)現(xiàn)惫搏,這種方式和map類型的初始化很像,都是采用冒號(hào)分隔蚕涤。Go語言盡可能的重用操作筐赔,不發(fā)明新的表達(dá)式,便于記憶和使用揖铜。
當(dāng)然你也可以只初始化子字段age茴丰,字段name使用默認(rèn)值的零值,如下面代碼所示天吓,仍然可以編譯通過贿肩。

p:=person{age:30}

子彈結(jié)構(gòu)體
結(jié)構(gòu)體的字段可以是任意類型,也包括自定義的結(jié)構(gòu)體類型龄寞。比如下面的代碼

type person struct {
    name string
    age uint
    addr address
}

type address struct {
    province string
    city string
}

在這個(gè)示例中汰规,定義了2個(gè)結(jié)構(gòu)體:person 表示人,address表示地址物邑,在結(jié)構(gòu)體person中溜哮,有一個(gè)address類型的字段addr滔金,這就是自定義的結(jié)構(gòu)體

通過這種方式,用代碼描述現(xiàn)實(shí)中的實(shí)體會(huì)更匹配茬射,復(fù)用程度也更高鹦蠕。對于嵌套結(jié)構(gòu)體字段的結(jié)構(gòu)體,其初始化和正常的結(jié)構(gòu)體大同小異在抛,只需要根據(jù)字段對應(yīng)的類型初始化即可钟病,如下面的代碼所示:

 p:=person{
        age:30,
        name:"飛雪無情",
        addr:address{
            province: "北京",
            city:     "北京",
        },
    }

如果需要訪問結(jié)構(gòu)體最里層的 province 字段的值,同樣也可以使用點(diǎn)操作符刚梭,只不過需要使用兩個(gè)點(diǎn)肠阱,如下面的代碼所示:

fmt.Println(p.addr.province)

第一個(gè)點(diǎn)獲取addr,第二個(gè)點(diǎn)獲取addr的province

接口
接口的定義
接口是和調(diào)用方的一種約定,他是一種高度抽象的類型朴读,不用和具體的實(shí)現(xiàn)細(xì)節(jié)綁定在一起屹徘。接口要做的是定義好約定,告訴調(diào)用方自己可以做點(diǎn)什么衅金,但不用知道他的內(nèi)部實(shí)現(xiàn)噪伊,這和我們見到的具體的類型如 int,map,slice等不一樣

接口的定義和結(jié)構(gòu)體稍微有些差別,雖然都以type關(guān)鍵字開始氮唯,但接口的關(guān)鍵字是interface鉴吹,表示自定義的類型是一個(gè)接口,也就是說Stringer是一個(gè)接口惩琉,他有一個(gè)方法String()string 整體如下面的代碼所示

type Stringer interface {
    String() string
}

提示: Stringer 是Go SDK的一個(gè)的接口豆励,屬于fmt包
針對Stringer接口來說,他會(huì)告訴調(diào)用者可以通過他的String()方法獲取一個(gè)字符串這就是接口的約定瞒渠。至于這個(gè)字符串怎么獲得的良蒸,長什么樣,接口不關(guān)心伍玖,調(diào)用者也不用關(guān)心嫩痰,因?yàn)檫@些是由接口實(shí)現(xiàn)者來做的。

接口的實(shí)現(xiàn)
接口的實(shí)現(xiàn)者必須是一個(gè)具體的類型窍箍,繼續(xù)以person結(jié)構(gòu)體為例始赎,讓他來實(shí)現(xiàn)Stringer接口,如下代碼所示

func (p person) String() string {
    return fmt.Sprintf("the name is %s,age is %d", p.age, p.age)
}

給結(jié)構(gòu)體類型Person定義一個(gè)方法仔燕,這個(gè)方法和接口里方法的簽名(名稱,參數(shù)和返回值)一樣魔招,這樣結(jié)構(gòu)體person就實(shí)現(xiàn)了Stringer接口
注意:如果一個(gè)接口有多個(gè)方法晰搀,那么需要實(shí)現(xiàn)接口的每個(gè)方法才算是實(shí)現(xiàn)了這個(gè)接口。
實(shí)現(xiàn)了Stringer接口后就可以使用了办斑,首先先來定義一個(gè)打印Stringer接口的函數(shù)外恕,如下所示:

func printString(s fmt.Stringer) {
    fmt.Println(s.String())
}

這個(gè)被定義的函數(shù)printString杆逗,他接收一個(gè)Stringer接口類型的參數(shù),然后打印出Stringer接口的String方法返回的字符串
printString 這個(gè)函數(shù)的優(yōu)勢在于他是面向接口編程的鳞疲,只要一個(gè)類型實(shí)現(xiàn)了Stringer接口罪郊,都可以打印出對應(yīng)的字符串,都可以打印出對應(yīng)的字符串尚洽,而不用管具體的類型實(shí)現(xiàn)
因?yàn)閜erson 實(shí)現(xiàn)了 Stringer接口悔橄,所以變量p可以作為函數(shù)printString,可以用如下方式打印:

func printString(s fmt.Stringer){
    fmt.Println(s.String())
}

這個(gè)被定義的函數(shù)pringString腺毫,它接收了一個(gè)Stringer接口類型的參數(shù)癣疟,然后打印出Stringer接口的String 方法返回的字符串。

printString這個(gè)函數(shù)的優(yōu)勢就在于他是面向接口編程的潮酒,只要一個(gè)類型實(shí)現(xiàn)了Stringer接口睛挚,都可以打印出對應(yīng)的字符串,而不用管具體的類型實(shí)現(xiàn)
因?yàn)閜erson 實(shí)現(xiàn)了Stringer接口急黎,所以變量p可以作為函數(shù)printString 的參數(shù)扎狱,可以用如下方式打印:

printString(p)

結(jié)果為

the name is 飛雪無情,age is 30

現(xiàn)在讓結(jié)構(gòu)體address也實(shí)現(xiàn)了Stringer 接口勃教,如下代碼所示

func (addr address) String()  string{
    return fmt.Sprintf("the addr is %s%s",addr.province,addr.city)
}

因?yàn)榻Y(jié)構(gòu)體address也實(shí)現(xiàn)了Stringer接口淤击,所以pringString函數(shù)不用做任何改變,可以直接被使用荣回,打印出地址遭贸,如下所示:

printString(p.addr)
//輸出:the addr is 北京北京

這就是面向接口的好處,只要定義和調(diào)用方滿足約定心软,就可以使用壕吹,而不用管具體的實(shí)現(xiàn)。接口的實(shí)現(xiàn)者也可以更好的升級(jí)重構(gòu)删铃,而不會(huì)有任何的影響耳贬,因?yàn)榻涌诘募s定沒有變

值接收者和指針接收者

我們已經(jīng)知道,如果實(shí)現(xiàn)一個(gè)接口猎唁,必須實(shí)現(xiàn)這個(gè)接口提供的所有方法咒劲,而且定義一個(gè)方法,有值類型接收者和指針類型接收者2種诫隅。二者都可以調(diào)用方法腐魂,因?yàn)镚o語言編譯器自動(dòng)做了轉(zhuǎn)換,所以值類型接收者和指針類型接收者是等價(jià)的逐纬。但是在接口的實(shí)現(xiàn)中蛔屹,值類型接收者和指針類型接收者不一樣。

已經(jīng)驗(yàn)證了結(jié)構(gòu)體類型實(shí)現(xiàn)了 Stringer 接口豁生,那么結(jié)構(gòu)體對應(yīng)的指針是否也實(shí)現(xiàn)了該接口呢兔毒?我通過下面這個(gè)代碼進(jìn)行測試:

printString(&p)
測試后會(huì)發(fā)現(xiàn)漫贞,把變量 p 的指針作為實(shí)參傳給 printString 函數(shù)也是可以的,編譯運(yùn)行都正常育叁。這就證明了以值類型接收者實(shí)現(xiàn)接口的時(shí)候迅脐,不管是類型本身,還是該類型的指針類型豪嗽,都實(shí)現(xiàn)了該接口谴蔑。

示例中值接收者(p person)實(shí)現(xiàn)了Stringer接口,那么類型person和他的指針類型

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末昵骤,一起剝皮案震驚了整個(gè)濱河市树碱,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌变秦,老刑警劉巖成榜,帶你破解...
    沈念sama閱讀 211,376評論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異蹦玫,居然都是意外死亡赎婚,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,126評論 2 385
  • 文/潘曉璐 我一進(jìn)店門樱溉,熙熙樓的掌柜王于貴愁眉苦臉地迎上來挣输,“玉大人,你說我怎么就攤上這事福贞×媒溃” “怎么了?”我有些...
    開封第一講書人閱讀 156,966評論 0 347
  • 文/不壞的土叔 我叫張陵挖帘,是天一觀的道長完丽。 經(jīng)常有香客問我,道長拇舀,這世上最難降的妖魔是什么逻族? 我笑而不...
    開封第一講書人閱讀 56,432評論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮骄崩,結(jié)果婚禮上聘鳞,老公的妹妹穿的比我還像新娘。我一直安慰自己要拂,他們只是感情好抠璃,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,519評論 6 385
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著脱惰,像睡著了一般搏嗡。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上枪芒,一...
    開封第一講書人閱讀 49,792評論 1 290
  • 那天彻况,我揣著相機(jī)與錄音,去河邊找鬼舅踪。 笑死纽甘,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的抽碌。 我是一名探鬼主播悍赢,決...
    沈念sama閱讀 38,933評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼货徙!你這毒婦竟也來了左权?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,701評論 0 266
  • 序言:老撾萬榮一對情侶失蹤痴颊,失蹤者是張志新(化名)和其女友劉穎赏迟,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體蠢棱,經(jīng)...
    沈念sama閱讀 44,143評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡锌杀,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,488評論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了泻仙。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片糕再。...
    茶點(diǎn)故事閱讀 38,626評論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖玉转,靈堂內(nèi)的尸體忽然破棺而出突想,到底是詐尸還是另有隱情,我是刑警寧澤究抓,帶...
    沈念sama閱讀 34,292評論 4 329
  • 正文 年R本政府宣布猾担,位于F島的核電站,受9級(jí)特大地震影響漩蟆,放射性物質(zhì)發(fā)生泄漏垒探。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,896評論 3 313
  • 文/蒙蒙 一怠李、第九天 我趴在偏房一處隱蔽的房頂上張望圾叼。 院中可真熱鬧,春花似錦捺癞、人聲如沸夷蚊。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,742評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽惕鼓。三九已至,卻和暖如春唐础,著一層夾襖步出監(jiān)牢的瞬間箱歧,已是汗流浹背矾飞。 一陣腳步聲響...
    開封第一講書人閱讀 31,977評論 1 265
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留呀邢,地道東北人洒沦。 一個(gè)月前我還...
    沈念sama閱讀 46,324評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像价淌,于是被迫代替她去往敵國和親申眼。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,494評論 2 348

推薦閱讀更多精彩內(nèi)容