golang中接口賦值與方法集

接口使用疑問

golang中的接口可以輕松實現(xiàn)C++中的多態(tài),而且沒有繼承自同一父類的限制,感覺方便很多。但是在使用的時候,如果沒有理解,也可能會遇到"坑"。比如《Go語言實戰(zhàn)》中的一個例子:

package main

import "fmt"

type user struct {
    name  string
    email string
}
type notifier interface {
    notify()
}

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

func main() {
    u := user{
        name:  "stormzhu",
        email: "abc@qq.com",
    }
    sendNotification(u) 
}
// compile error
// cannot use u (type user) as type notifier in argument to sendNotification:
//  user does not implement notifier (notify method has pointer receiver)

報的錯是u沒有實現(xiàn)notifier這個接口,實現(xiàn)了這個接口的是*user類型,而不是user類型,uuser類型,所以不能賦值給notifier這個接口。

既然如此,修改為sendNotification(&u)就OK了。然而問題是,如何理解到底是T類型還是*T類型實現(xiàn)了某個接口呢?

接口的定義

參考雨痕的《Go語言學(xué)習(xí)筆記》第七章,go語言中的接口定義如下:

type iface struct {
    tab  *itab          // 類型信息
    data unsafe.Pointer //實際對象指針
}
type itab struct {
    inter *interfacetype // 接口類型
    _type *_type         // 實際對象類型
    fun   [1]uintptr     // 實際對象方法地址
}

雖然具體的細節(jié)操作不太懂,但是可以知道,對一個接口賦值的時候,會拷貝類型信息和該類型的方法集。這就類似于C++多態(tài)中的虛指針(vptr)和虛函數(shù)表(vtable)了。我理解的是,只要這個類型的方法集中包括這個接口的所有方法,那么它就是實現(xiàn)了這個接口,才能夠賦值給這個接口,那么問題來了,一個類型的方法集是什么呢?

方法集

同樣參考雨痕《Go語言學(xué)習(xí)筆記》第6章6.3節(jié),書中總結(jié)的很全面:

  • 類型T的方法集包含所有 receiver T方法。
  • 類型*T的方法集包含所有 receiver T + *T方法。
  • 匿名嵌入S,類型T的方法集包含所有 receiver T + S方法。
  • 匿名嵌入*S,類型T的方法集包含所有 receiver T + S + *S方法。
  • 匿名嵌入S*S,類型*T的方法集包含所有 receiver T + *T + S + *S方法。

雖然看起來比較復(fù)雜,但總結(jié)完就一話,*T類型就是厲害,方法集包括T*T的方法。

所以文章開頭的例子中,uuser類型,方法集是空的,不算是實現(xiàn)了notifier接口。

當(dāng)在糾結(jié)應(yīng)該將T類型還是*T類型賦值給某個接口的時候,第一步就是看方法集,看一看該類型到底有沒有實現(xiàn)這個接口。(所以T*T不是一個類型。。。)

一些例子

go語言的內(nèi)置庫中有定義了很多接口,如error接口,

type error interface {
    Error() string
}

內(nèi)置的errors包實現(xiàn)了這個接口:

// Package errors implements functions to manipulate errors.
package errors

// New returns an error that formats as the given text.
func New(text string) error {
    return &errorString{text}
}

// errorString is a trivial implementation of error.
type errorString struct {
    s string
}

func (e *errorString) Error() string {
    return e.s
}

可以看到New方法返回值是error接口,而只有*errorString類型實現(xiàn)了這個接口,所以New方法返回的是&errorString{text}而不是errorString{text}

總結(jié)

  • T*T不是一個類型,他們的方法集不同
  • 類型*T的方法集包含所有 receiver T + *T方法,類型T的方法集只包含所有 receiver T方法。

我的SegmentFault博客

參考

最后編輯于
?著作權(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)容

  • 1. Java基礎(chǔ)部分 基礎(chǔ)部分的順序:基本語法,類相關(guān)的語法,內(nèi)部類的語法,繼承相關(guān)的語法,異常的語法,線程的語...
    子非魚_t_閱讀 34,623評論 18 399
  • 概述在 Go 語言中,如果一個結(jié)構(gòu)體和一個嵌入字段同時實現(xiàn)了相同的接口會發(fā)生什么呢?我們猜一下,可能有兩個問題: ...
    isgiker閱讀 18,907評論 1 13
  • 每個城市都有一個屬于自己的味道,很多人不相信,但是,是真的。 比如說,斯德哥爾摩有一種淡淡的黑松露的清香,巴黎是那...
    野霧啦吠放閱讀 481評論 6 4
  • 揭秘高檔化妝品怎么利用灰色套路賺100...
    圣手農(nóng)夫閱讀 396評論 0 0
  • 兒子有一個發(fā)小,那孩子機靈敏捷,從小學(xué)就透出一種天賦,對文字過目不忘。 小學(xué)時,周末他都在我家渡過的。他們倆喜歡在...
    向日葵3閱讀 958評論 0 2

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