Go In Action --- 類型的本質(zhì)赞庶、接口你辣、多態(tài)、嵌入類型尘执、標(biāo)識(shí)符

類型

在聲明一個(gè)新類型之后舍哄,聲明一個(gè)該類型的方法之前,需先確定:這個(gè)類型的本質(zhì)是什么誊锭?如果給這個(gè)類型增加或刪除某個(gè)值表悬,是要?jiǎng)?chuàng)建一個(gè)新值,還是要更改當(dāng)前的值丧靡?如果要新值蟆沫,就選擇值接收者;如果要修改當(dāng)前值温治,就選擇指針接收者饭庞。這決定了 程序內(nèi)部傳遞這個(gè)類型的值的方式:是按值做傳遞,還是按指針做傳遞熬荆。

1. 內(nèi)置類型

包含數(shù)值類型舟山、字符串類型和布爾類型
這些類型本質(zhì)上都是原始類型,在操作其值時(shí)累盗,應(yīng)該傳遞其對(duì)應(yīng)的值的副本寒矿。

2. 引用類型

包含切片、映射若债、通道符相、接口和函數(shù)類型
每個(gè)引用類型創(chuàng)建的標(biāo)頭值(引用類型創(chuàng)建的變量)蠢琳,都包含一個(gè)指向底層數(shù)據(jù)結(jié)構(gòu)的指針啊终。
所以通過復(fù)制來傳遞一個(gè)引用類型的值的副本,本質(zhì)上就是在共享底層數(shù)據(jù)結(jié)構(gòu)傲须。

3. 結(jié)構(gòu)類型

待補(bǔ)充
結(jié)構(gòu)類型的本質(zhì)既可以是原始的蓝牲,也可以是非原始的。

4. 看一個(gè)標(biāo)準(zhǔn)庫中的接口 及 實(shí)現(xiàn)例子

4.1 io.Writer接口

// Writer is the interface that wraps the basic Write method.
//
// Write writes len(p) bytes from p to the underlying data stream.
// It returns the number of bytes written from p (0 <= n <= len(p))
// and any error encountered that caused the write to stop early.
// Write must return a non-nil error if it returns n < len(p).
// Write must not modify the slice data, even temporarily.
//
// Implementations must not retain p.
type Writer interface {
    Write(p []byte) (n int, err error)
}

4.2 bytes.Buffer實(shí)現(xiàn)了該接口

// Write appends the contents of p to the buffer, growing the buffer as
// needed. The return value n is the length of p; err is always nil. If the
// buffer becomes too large, Write will panic with ErrTooLarge.
func (b *Buffer) Write(p []byte) (n int, err error) {
    b.lastRead = opInvalid
    m := b.grow(len(p))
    return copy(b.buf[m:], p), nil
}

4.3 接口實(shí)現(xiàn)多態(tài)

當(dāng)方法的接收參數(shù)是某個(gè)接口時(shí)躏碳,可以將不同的實(shí)現(xiàn)該接口的值作為參數(shù)傳遞給方法搞旭,從而實(shí)現(xiàn)采取不同行為的能力。
方法fmt.Fprintf的第一個(gè)參數(shù)是io.Writer接口

// Fprintf formats according to a format specifier and writes to w.
// It returns the number of bytes written and any write error encountered.
func Fprintf(w io.Writer, format string, a ...interface{}) (n int, err error) {
    p := newPrinter()
    p.doPrintf(format, a)
    n, err = w.Write(p.buf)
    p.free()
    return
}

方法io.Copy的第一個(gè)參數(shù)也是io.Writer接口

func Copy(dst Writer, src Reader) (written int64, err error) {
    return copyBuffer(dst, src, nil)
}

4.4 簡(jiǎn)單的例子展示io多態(tài)

func ByteBuffer()  {
    var b bytes.Buffer

    //將字符串寫入Buffer
    b.Write([]byte("Hello go"))

    //使用Fprintf將字符串拼接到Buffer
    fmt.Fprintf(&b, "菇绵, nice to meet you")

    //將Buffer的內(nèi)容寫到Stdout
    io.Copy(os.Stdout, &b)
}

最終輸出結(jié)果:
Hello go肄渗, nice to meet you

5. Go語言的接口規(guī)則

因?yàn)樯婕暗街羔槪?guī)則相比Java來說略微復(fù)雜一些咬最。

Go語言的方法有接收者這一說詞翎嫡,從Java的角度來看,就是方法的調(diào)用者永乌。在Java中惑申,調(diào)用者就是調(diào)用者。但是Go語言中翅雏,分值接收者指針接收者圈驼。但規(guī)則不外乎如下兩種:

  1. 如果一個(gè)接口的實(shí)現(xiàn)類型,用指針接收者來作為方法的接收者(其實(shí)就是Java的調(diào)用者)望几,那么只能用該類型的指針對(duì)象來調(diào)用該接口绩脆。

  2. 如果一個(gè)接口的實(shí)現(xiàn)類型,用值接收者來作為方法的接收者橄抹,那么既可以用指針對(duì)象也可以用值的副本來調(diào)用該接口靴迫。

以下是Go語言給出的方法集規(guī)則:

Values Methods Receivers remark
T (t T) 如2
*T (t T) and (t *T) 如1
Methods Receivers Values remark
(t T) T and *T 如2
(t *T) *T 如1

5. 多態(tài)

如下實(shí)現(xiàn)了一個(gè)簡(jiǎn)單的多態(tài):

//person接口
type personInter interface {
    sayName()
}

//用戶
type user struct {
    name string
}
func (u *user) sayName()  {
    fmt.Printf("User name is %s \n", u.name)
}

//管理員
type admin struct {
    name string
}
func (u *admin) sayName()  {
    fmt.Printf("Admin name is %s \n", u.name)
}

func Test()  {
    logger := user{"logger"}
    //letsSayYourName(logger)  //這是錯(cuò)誤調(diào)用,不能用值的副本logger去調(diào)用接收者為指針接收者的實(shí)現(xiàn)方法
    letsSayYourName(&logger)

    admin := admin{"admin"}
    letsSayYourName(&admin)
}

//方法入?yún)榻涌诼ナ模灰獙?shí)現(xiàn)了該接口的實(shí)現(xiàn)類型都可作為入?yún)?func letsSayYourName(person personInter)  {
    person.sayName()
}

最終輸出:
User name is logger
Admin name is admin

6. 嵌入類型

先介紹一下Go語言所謂的嵌入類型:
嵌入類型是將已有的類型直接聲明在新的結(jié)構(gòu)類型中玉锌,被嵌入的類型被稱為新的外部類型的內(nèi)部類型。
個(gè)人理解Go語言中的嵌入類型類似于Java中的組合疟羹。

6.1 示例1 -- 外部類型調(diào)用內(nèi)嵌類型的方法

type animal struct {
    name string
}

func (u *animal) sayName() {
    fmt.Printf("name is %s \n", u.name)
}

//管理員類型主守,內(nèi)嵌一個(gè)user類型
type person struct {
    animal  //嵌入類型
    name string
}

func TestInnerType() {
    ad := person{animal{"logger"}, "admin"}
    ad.animal.sayName()
    ad.sayName()
}

輸出:
name is logger
name is logger

小結(jié):

  • 可以將內(nèi)嵌類型的方法提升到外部類型
  • ad.sayName()調(diào)用內(nèi)嵌類型的方法輸出的還是內(nèi)嵌類型的屬性

6.2 示例2 -- 外部類型調(diào)用內(nèi)嵌類型的實(shí)現(xiàn)接口

//接口
type foodInter interface {
    sayFoodName()
}

//蘋果
type apple struct {
    name string
}

//超市
type market struct {
    apple  //內(nèi)嵌類型 -- 蘋果
}

func (a *apple) sayFoodName()  {
    fmt.Printf("food name is %s \n", a.name)
}

func TestInnerTypeInterface()  {
    walmart := market{ apple{"ApplyX"}}

    //重點(diǎn)是這句
    //用于實(shí)現(xiàn)接口的內(nèi)部類型的方法禀倔,被提升到了外部類型
    commonSayName(&walmart)
}

func commonSayName(food foodInter)  {
    food.sayFoodName()
}

輸出:
food name is ApplyX

小結(jié):

  • 由于內(nèi)部類型的提升,內(nèi)部類型實(shí)現(xiàn)的接口會(huì)自動(dòng)提升到外部類型丸逸。這意味著由于內(nèi)部類型的實(shí)現(xiàn)蹋艺,外部類型也同樣實(shí)現(xiàn)了這個(gè)接口剃袍。

6.3 示例3 -- 外部類型覆蓋內(nèi)嵌類型的實(shí)現(xiàn)接口

相比于示例2黄刚,增加了類型market自己的實(shí)現(xiàn)方法以此覆蓋內(nèi)嵌類型的實(shí)現(xiàn)接口,并在類型market中添加了屬性marketName以便輸出展示民效。

//接口
type foodInter interface {
    sayFoodName()
}

//蘋果
type apple struct {
    name string
}

//超市
type market struct {
    marketName string
    apple  //內(nèi)嵌類型 -- 蘋果
}

func (a *apple) sayFoodName()  {
    fmt.Printf("food name is %s \n", a.name)
}

func (m *market) sayFoodName()  {
    fmt.Printf("market name is %s \n", m.marketName)
}

func TestInnerTypeInterface()  {
    walmart := market{ "walmart", apple{"ApplyX"}}

    //重點(diǎn)是這句
    //用于實(shí)現(xiàn)接口的內(nèi)部類型的方法憔维,被提升到了外部類型
    commonSayName(&walmart)
}

func commonSayName(food foodInter)  {
    food.sayFoodName()
}

輸出:
market name is walmart

小結(jié):

  • 由于內(nèi)部類型的提升,內(nèi)部類型實(shí)現(xiàn)的接口會(huì)自動(dòng)提升到外部類型畏邢。這意味著由于內(nèi)部類型的實(shí)現(xiàn)业扒,外部類型也同樣實(shí)現(xiàn)了這個(gè)接口。

7. 公開或未公開的標(biāo)識(shí)符

類似于Java中的public舒萎、private等程储,你可以控制方法或變量的訪問權(quán)限。Go語言當(dāng)然也提供了這種功能臂寝。
Go語言支持從包里公開或隱藏標(biāo)識(shí)符章鲤。如果一個(gè)標(biāo)識(shí)符,如下示例中的counter以小寫字母開頭咆贬,即包外代碼不可見(類似Java的private)败徊,如果一個(gè)標(biāo)識(shí)符,如下示例中的PublicCounter以大寫字母開頭掏缎,這個(gè)標(biāo)識(shí)符就是公開的皱蹦,即包外代碼可見(類似Java的public)。

7.1 原始類型

//聲明一個(gè)未公開的計(jì)數(shù)器
type counter int

//聲明一個(gè)公開的計(jì)數(shù)器
type PublicCounter int

當(dāng)然眷蜈,我們也可以給未公開的標(biāo)識(shí)符沪哺,如counter寫一個(gè)公開的取值函數(shù),或者寫一個(gè)工廠函數(shù)新建一個(gè)counter酌儒。

//取值函數(shù)
func GetCounter(value int) counter {
  return counter
}

//工廠函數(shù)
func New(value int) counter {
  return counter(value)
}

工廠函數(shù)命名為New只是Go語言中的一個(gè)習(xí)慣辜妓。

7.2 結(jié)構(gòu)類型中的未公開字段

結(jié)構(gòu)類型中帶有未公開類型的字段

//公開的用戶類型
type Admin struct {
    Name string    //公開的Name字段
    email string    //未公開的email字段
}

如果在其它包中創(chuàng)建該類型,并賦值email字段今豆,在定義階段是不會(huì)報(bào)錯(cuò)的嫌拣,如下不會(huì)直接報(bào)錯(cuò)(identifier是包名):

ad := identifier.Admin{"admin", "admin@email.com"}

但是在運(yùn)行的時(shí)候,會(huì)報(bào)錯(cuò)呆躲,錯(cuò)誤如下:

# command-line-arguments
.\main.go:43: implicit assignment of unexported field 'email' in identifier.Admin literal

7.3 訪問未公開的內(nèi)嵌對(duì)象中的公開字段

identifier包中定義如下結(jié)構(gòu)類型异逐,apple是未公開的結(jié)構(gòu)類型,但是其Name字段是公開的插掂。

type apple struct {
    Name string
}

//公開類型的Tree對(duì)象
type Tree struct {
    TreeName string //公開類型的TreeName
    apple           //未公開的apple類型
}

在其它包中灰瞻,可以利用內(nèi)部對(duì)象屬性升級(jí)為外部對(duì)象的特性腥例,為tree的未公開內(nèi)嵌類型apple的公開字段Name設(shè)值。

tree := identifier.Tree{TreeName: "MoneyTree"}
tree.Name = "apply"
fmt.Printf("%v \n", tree)
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末酝润,一起剝皮案震驚了整個(gè)濱河市燎竖,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌要销,老刑警劉巖构回,帶你破解...
    沈念sama閱讀 206,378評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異疏咐,居然都是意外死亡纤掸,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,356評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門浑塞,熙熙樓的掌柜王于貴愁眉苦臉地迎上來借跪,“玉大人,你說我怎么就攤上這事酌壕√统睿” “怎么了?”我有些...
    開封第一講書人閱讀 152,702評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵卵牍,是天一觀的道長(zhǎng)果港。 經(jīng)常有香客問我,道長(zhǎng)辽慕,這世上最難降的妖魔是什么京腥? 我笑而不...
    開封第一講書人閱讀 55,259評(píng)論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮溅蛉,結(jié)果婚禮上公浪,老公的妹妹穿的比我還像新娘。我一直安慰自己船侧,他們只是感情好欠气,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,263評(píng)論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著镜撩,像睡著了一般预柒。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上袁梗,一...
    開封第一講書人閱讀 49,036評(píng)論 1 285
  • 那天宜鸯,我揣著相機(jī)與錄音,去河邊找鬼遮怜。 笑死淋袖,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的锯梁。 我是一名探鬼主播即碗,決...
    沈念sama閱讀 38,349評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼焰情,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了剥懒?” 一聲冷哼從身側(cè)響起内舟,我...
    開封第一講書人閱讀 36,979評(píng)論 0 259
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎初橘,沒想到半個(gè)月后验游,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,469評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡壁却,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,938評(píng)論 2 323
  • 正文 我和宋清朗相戀三年批狱,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了裸准。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片展东。...
    茶點(diǎn)故事閱讀 38,059評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖炒俱,靈堂內(nèi)的尸體忽然破棺而出盐肃,到底是詐尸還是另有隱情,我是刑警寧澤权悟,帶...
    沈念sama閱讀 33,703評(píng)論 4 323
  • 正文 年R本政府宣布砸王,位于F島的核電站,受9級(jí)特大地震影響峦阁,放射性物質(zhì)發(fā)生泄漏谦铃。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,257評(píng)論 3 307
  • 文/蒙蒙 一榔昔、第九天 我趴在偏房一處隱蔽的房頂上張望驹闰。 院中可真熱鬧,春花似錦撒会、人聲如沸嘹朗。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,262評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽屹培。三九已至,卻和暖如春怔檩,著一層夾襖步出監(jiān)牢的瞬間褪秀,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評(píng)論 1 262
  • 我被黑心中介騙來泰國(guó)打工薛训, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留媒吗,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,501評(píng)論 2 354
  • 正文 我出身青樓许蓖,卻偏偏與公主長(zhǎng)得像蝴猪,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子自阱,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,792評(píng)論 2 345

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