《Go in Action》第五章 類型系統(tǒng)

首先间驮,go是一門靜態(tài)類型語言磕洪。即編譯器知道每一個值的類型焙糟。

如何定義一個類型炬藤?

A: 使用關(guān)鍵字type和struct:

type user struct {
  name string
  email string
  ext int
  privileged bool
}

注意每一行沒有逗號。
創(chuàng)建一個類型的值:

var rain user
lisa := user {
  nae: "Lisa",
  email: "lisa@email.com",
  ext: 123,
  privileged: true,
}

lisa1 := user{"Lisa", "lisa@email.com", 123, true} //最后一個逗號沒有

注意每一行都有逗號饺窿,最后一行也有歧焦。

struct的屬性可以是struct類型:

type admin struct {
  u user
  level string
}

fred := admin {
  u: user {
    name: "Fred",
    email: "fred@email.com",
    ext: 123,
    privileged: true,
  },
  level: "super",
}

另一種創(chuàng)建類型的方式:

type Duration int64

注意此時,編譯器認為Duration和int64不是同一種類型肚医。此時绢馍,我們稱int64是Duration的"base type"

方法

方法是一種給類型增加行為的方式。

type user struct {
  name string
  email string
}

func (u user) notify() {
  fmt.Printf("Sending User Email to %s<%s>\n", u.name, u.email)
}

func (u *user) changeEmail(email string) {
  u.email = email
}

func和方法名之間的參數(shù)稱為“receiver"忍宋。當(dāng)一個函數(shù)具有receiver的時候痕貌,我們就稱該函數(shù)為方法。

receiver具有兩種類型:

  • value receiver糠排。即值傳遞舵稠。
  • pointer receiver。傳遞的是類型的指針

兩個不同類型的方法都可以被類型的值或者指針調(diào)用入宦。如:

bill := user{"bill", "bill@email.com"}
bill.notify()

lisa := &user{"lisa", "lisa@email.com"}
lisa.changeEmail("lisayou")
lisa.notify()

go會自動進行(*lisa).notify()(&bill).changeEmail()的轉(zhuǎn)換哺徊。

一般來說,當(dāng)使用value receiver的時候表明不需要改變值乾闰,使用pointer receiver的時候需要改變值落追。

接口

A: 使用type和interface定義接口:

type notifier interface{
  notify()
}

注意這里沒有定義接口的返回值和參數(shù),因為返回值和參數(shù)都沒有涯肩。

Q: 如何使用接口轿钠?

A: 見代碼:

func sendNotification(n notifier) {
  n.notify()
}

Q: 如何實現(xiàn)接口巢钓?

A: 類型的方法具有同接口一樣的名字、參數(shù)和返回值疗垛,就認為該類型實現(xiàn)了接口症汹。無需顯示聲明。
比如:

type user struct {
  name string
  email string
}

func (u user) notify(){

}

此時user實現(xiàn)了notifier接口贷腕。

Q: point receiver的方法算接口實現(xiàn)嗎背镇?

A: 看一個例子:

type user struct {
  name string
  email string
}
func (u *user) notify(){
}

此時user算是實現(xiàn)了notifier了嗎?
先來了解一下methods sets:

Method sets define the set of methods that are associated with values or pointers of a given type. The type of receiver used will determine whether a method is associated with a value, pointer, or both.

method sets定義了一個值或者指針類型的方法集泽裳。方法的receiver的類型決定了一個方法是同值瞒斩、指針或者兩者都進行了關(guān)聯(lián)。聽起來一頭霧水涮总。

image.png

這個表的意思是:

  • 類型T的值的method sets僅包含value receiver的方法
  • 類型*T的值的method sets既包含value receiver的方法也包含pointer receiver的方法

從method receiver的角度來看:


image.png

這個的意思就是:

  • 如果使用pointer receiver實現(xiàn)了某個接口胸囱,那么實際上是該類型的指針實現(xiàn)了該接口
  • 如果使用value receiver實現(xiàn)了某個接口,那么該類型的值和指針類型都實現(xiàn)了該接口

上面的代碼的方法是pointer receiver瀑梗,所以僅有user類型的指針類型實現(xiàn)了接口旺矾。

為什么這么定義,是因為并不是總能獲取某個值的地址:

type duration int

func (d *duration) pretty() string {
  return fmt.Sprintf("Duration: %d", *d)
}

func main() {
  duration(42).pretty()
}

Q: 什么是多態(tài)夺克?

A: 簡單來說:多態(tài)就是,聲明的時候是接口嚎朽,傳入的時候是該接口的實現(xiàn)铺纽。不同的實現(xiàn)具有不同的行為。

Q: type embedding是什么哟忍?

A: 先看原文:

This is accomplished through type embedding. It works by taking an existing type and declaring that type within the declaration of a new struct type. The type that is embedded is then called an inner type of the new outer type.

簡單來說狡门,就是將一個已經(jīng)存在的類型(type)放在一個新類型中聲明。
被嵌入的類型稱為inner type锅很,新類型稱為outer type其馏。

有什么作用呢?

Through inner type promotion, identifiers from the inner type are promoted up to the outer type. These promoted identifiers become part of the outer type as if they were declared explicitly by the type itself.

簡單來說爆安,inner type的屬性和方法就像是outer type自己定義的一樣叛复。同時outer type也可以覆蓋inner type的屬性和方法∪硬郑看一個例子:

package main

import (
  "fmt"
)

type user struct {
  name string
  emaill string
}

func (u *user) notify() {
  fmt.Printf("Sending user email to %s<%s>\n", u.name, u.email)
}

type admin struct {
  user  // Embedded Type
  level string
}

func main() {
  ad := admin{
    user: user{
      name: "john smith",
      email: "john@yahoo.com",
    },
    level: "super",
  }
  ad.user.notify()
  ad.notify()
}

我們也可以覆蓋user里面的方法褐奥,比如:

func (a *admin) notify() {
  fmt.Printf("Sending admin email to %s<%s>\n", a.name, a.email)
}

此時,由于admin內(nèi)嵌了user翘簇,user實現(xiàn)了notifier接口撬码,因此admin其實也實現(xiàn)了notifier接口。

導(dǎo)出和不導(dǎo)出identifiers

簡單來說版保,就是包里面的小寫字母開頭的標(biāo)識符是不導(dǎo)出的呜笑,大寫開頭的是導(dǎo)出的夫否。
先來看一個例子:

// counters/counters.go
package counters

type alertCounter int

func New(value int) alertCounter {
  return alertCounter(value)
}

// listing68.go
package main

import (
  "fmt"
  "github.com/goinaction/code/chapter5/listing68/counters"
)

func main() {
  counter := counters.New(10)
  fmt.Printf("Counter: %d\n", counter)
}

在counters.go里面的alertCounter是小寫字母開頭,因此該identifer沒有導(dǎo)出叫胁,不能在下個文件的main函數(shù)里面使用凰慈。但是New方法是導(dǎo)出了的。因此曹抬,main函數(shù)里面的代碼沒有錯誤溉瓶。

但是有個問題,alertCounter沒有導(dǎo)出谤民,怎么可以使用呢堰酿?
見原文:

This is possible for two reasons. First, identifiers are exported or unexported, not values. Second, the short variable declaration operator is capable of inferring the type and creating a variable of the unexported type. You can never explicitly create a variable of an unexported type, but the short variable declaration operator can.

簡單來說:
1,導(dǎo)出或者不導(dǎo)出的是identifiers张足,不是values触创。也就是說小寫字母的類型的值是可以在package外使用的;
2为牍,不能顯式使用未導(dǎo)出的identifier哼绑,但是可以隱式使用。也就是使用:=是可以的碉咆。

看一個完整版:

// entities/entities.go
package entities

type user struct{
  Name string
  Email string
}

type admin struct{
  user
  Rights int
}

// listing74.go
package main

import (
  "fmt"
  "github.com/goinaction/code/chapter5/listing74/entities"
)

func main() {
  a := entities.Admin{
    Rights: 10,
  }
  
  a.Name = "Bill"
  a.Email = "bill@email.com"
  fmt.Printf("User: %v\n", a)
}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末抖韩,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子疫铜,更是在濱河造成了極大的恐慌茂浮,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,470評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件壳咕,死亡現(xiàn)場離奇詭異席揽,居然都是意外死亡,警方通過查閱死者的電腦和手機谓厘,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,393評論 3 392
  • 文/潘曉璐 我一進店門幌羞,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人竟稳,你說我怎么就攤上這事属桦。” “怎么了住练?”我有些...
    開封第一講書人閱讀 162,577評論 0 353
  • 文/不壞的土叔 我叫張陵地啰,是天一觀的道長。 經(jīng)常有香客問我讲逛,道長亏吝,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,176評論 1 292
  • 正文 為了忘掉前任盏混,我火速辦了婚禮蔚鸥,結(jié)果婚禮上惜论,老公的妹妹穿的比我還像新娘。我一直安慰自己止喷,他們只是感情好馆类,可當(dāng)我...
    茶點故事閱讀 67,189評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著弹谁,像睡著了一般乾巧。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上预愤,一...
    開封第一講書人閱讀 51,155評論 1 299
  • 那天沟于,我揣著相機與錄音,去河邊找鬼植康。 笑死旷太,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的销睁。 我是一名探鬼主播供璧,決...
    沈念sama閱讀 40,041評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼冻记!你這毒婦竟也來了睡毒?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,903評論 0 274
  • 序言:老撾萬榮一對情侶失蹤冗栗,失蹤者是張志新(化名)和其女友劉穎吕嘀,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體贞瞒,經(jīng)...
    沈念sama閱讀 45,319評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,539評論 2 332
  • 正文 我和宋清朗相戀三年趁曼,在試婚紗的時候發(fā)現(xiàn)自己被綠了军浆。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,703評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡挡闰,死狀恐怖乒融,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情摄悯,我是刑警寧澤赞季,帶...
    沈念sama閱讀 35,417評論 5 343
  • 正文 年R本政府宣布,位于F島的核電站奢驯,受9級特大地震影響申钩,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜瘪阁,卻給世界環(huán)境...
    茶點故事閱讀 41,013評論 3 325
  • 文/蒙蒙 一撒遣、第九天 我趴在偏房一處隱蔽的房頂上張望邮偎。 院中可真熱鬧,春花似錦义黎、人聲如沸禾进。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,664評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽泻云。三九已至,卻和暖如春狐蜕,著一層夾襖步出監(jiān)牢的瞬間宠纯,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,818評論 1 269
  • 我被黑心中介騙來泰國打工馏鹤, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留征椒,地道東北人。 一個月前我還...
    沈念sama閱讀 47,711評論 2 368
  • 正文 我出身青樓湃累,卻偏偏與公主長得像勃救,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子治力,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,601評論 2 353

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