<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
我們都知道介面給類提供了一種多型的機制,什麼是多型,多型就是系統根據型別的具體實現完成不同的行為。
以下程式碼簡單說明了介面的作用
package main import ( "fmt" "io" "net/http" "os" ) // init 在main 函數之前呼叫 func init() { if len(os.Args) != 2 { fmt.Println("Usage: ./example2 <url>") os.Exit(-1) } } // main 是應用程式的入口 func main() { // 從Web 伺服器得到響應 r, err := http.Get(os.Args[1]) if err != nil { fmt.Println(err) return } // 從Body 複製到Stdout io.Copy(os.Stdout, r.Body) if err := r.Body.Close(); err != nil { fmt.Println(err) } }
①注意下 http.Get(os.Args[1]) 這裡他的返回值r是一個Response物件的指標,也就是請求的結果
做過web開發的都知道,下面是原始碼
func Get(url string) (resp *Response, err error) { return DefaultClient.Get(url) }
以下是Response的結構,這裡有一個Body,是一個io.ReadCloser型別的,這是啥?往下看
type Response struct { Status string // e.g. "200 OK" StatusCode int // e.g. 200 Proto string // e.g. "HTTP/1.0" ProtoMajor int // e.g. 1 ProtoMinor int // e.g. 0 Header Header Body io.ReadCloser ContentLength int64 TransferEncoding []string Close bool Uncompressed bool Trailer Header Request *Request TLS *tls.ConnectionState }
ReadCloser是一個介面哦!Reader和Closer也同樣是介面,介面裡面都是方法。
type ReadCloser interface { Reader Closer }
Reader介面
type Reader interface { Read(p []byte) (n int, err error) }
Closer介面
type Closer interface { Close() error }
②io.Copy(os.Stdout, r.Body) 這個方法,檢視原始碼如下
func Copy(dst Writer, src Reader) (written int64, err error) { return copyBuffer(dst, src, nil) }
這裡的輸入引數dst是一個實現了Writer介面的物件,而src則是一個實現了Reader介面的物件,由此,我們可以知道為什麼io.Copy(os.Stdout, r.Body)的第二個引數可以傳r.Body了,因為①中展示了Body這個物件是實現了Reader介面的。同理os.Stdout物件這個介面值表示標準輸出裝置,並且已經實現了io.Writer 介面
os.Stdout返回的是一個*File, File裡面只有一個*file,而*file是實現了下面兩個介面的,下面是Go的原始碼
func (f *File) Read(b []byte) (n int, err error) { if err := f.checkValid("read"); err != nil { return 0, err } n, e := f.read(b) return n, f.wrapErr("read", e) } func (f *File) Write(b []byte) (n int, err error) { if err := f.checkValid("write"); err != nil { return 0, err } n, e := f.write(b) if n < 0 { n = 0 } if n != len(b) { err = io.ErrShortWrite } epipecheck(f, e) if e != nil { err = f.wrapErr("write", e) } return n, err }
所以說*File本身是繼承了Writer和Reader介面的型別。
綜上有了引數或者返回值是介面型別,就不用關注於具體的返回型別是什麼,只要實現了的介面的方法都是可以被接受的。
我們都知道 如果一個型別實現了某個介面,那麼這個型別的實際值是可以賦值給一個介面的變數的。
在C#中是這樣的,例如將一個List賦值給一個IEnumerable型別的變數
IEnumerable<int> list = new List<int>();
在Go語言中也是這樣的,請看下面的程式碼
package main import ( "fmt" ) type eat interface{ eat()(string) } type Bird struct { Name string } func (bird Bird) eat()string{ return "Bird:"+bird.Name+" eat" } func print(e eat){ fmt.Println(e.eat()) } // main 是應用程式的入口 func main() { bird1:= Bird{Name:"Big"} bird2:= new(Bird) bird2.Name = "Small" print(bird1) print(bird2) var eat1 eat eat1 = bird1 print(eat1) }
結果
Bird:Big eat
Bird:Small eat
Bird:Big eat
這裡定義了一個eat介面,有一個Bird的型別實現了該介面,print函數接受一個eat介面型別的引數,
這裡可以看到前兩次直接把bird1和bird2作為引數傳入到print函數內,第二次則是宣告了一個eat介面型別的變數eat1,然後將bird1進行了賦值。換句話說介面型別變數實際承載了實際型別值。這裡是如何承載的呢?
這裡我們把 eat1 稱作 介面值,將bird1稱作實體型別值,eat1和bird1的關係如下:
介面值可以看成兩部分組合(都是指標)而成的。第一部分是【iTable的地址】第二部分是【實體型別值的地址】
關於interface的解析:
https://www.jb51.net/article/255284.htm
簡單的講:方法集定義了介面的接受規則
舉例說明:
package main import ( "fmt" ) type notifier interface { notify() } 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 sendNotification(n notifier) { n.notify() } func main() { u := user{"Bill", "bill@email.com"} sendNotification(u) }
如上程式碼,定義了一個notifier介面,有一個方法nitify()方法,定義了一個user型別的結構,實現了notify方法,接收者型別是user,即實現了notifier介面,又定義了一個sendNotification方法,接收一個實現notifier介面的型別,並呼叫notify方法。
func (u *user) notify() { fmt.Printf("Sending user email to %s<%s>n", u.name, u.email) } func main() { u := user{"Bill", "bill@email.com"} sendNotification(u) }
現在修正一下程式碼,將接收者改為user的指標型別。此時會發現原來呼叫的地方會出現錯誤。
cannot use u (type user) as type notifier in argument to sendNotification:user does not implement notifier (notify method has pointer receiver)
不能將u(型別是user)作為sendNotification 的引數型別notifier:user 型別並沒有實現notifier(notify 方法使用指標接收者宣告)
為什麼會出現上面的問題?要了解用指標接收者來實現介面時為什麼user 型別的值無法實現該介面,需要先了解方法集。方法集定義了一組關聯到給定型別的值或者指標的方法。
定義方法時使用的接收者的型別決定了這個方法是關聯到值,還是關聯到指標,還是兩個都關聯。
以下是Go語言規範中的方法集:
上表的意思是:型別的值只能實現值接收者的介面;指向型別的指標,既可以實現值接收者的介面,也可以實現指標接收者的介面。
從接收者的角度來看一下這些規則
如果是值接收者,實體型別的值和指標都可以實現對應的介面;如果是指標接收者,那麼只有型別的指標能夠實現對應的介面。
所以針對上面的問題,將傳入的u變成傳入地址就可以了(可以套用一下表格,接收者*user對應的values是*user,所以傳地址對應上面表格淺藍色部分)
func (u *user) notify() { fmt.Printf("Sending user email to %s<%s>n", u.name, u.email) } func main() { u := user{"Bill", "bill@email.com"} sendNotification(&u) }
綜上我們總結一下,也就是說如果你的方法的接受者的型別是指標型別,那麼方法的實現者就只能是指向該型別的指標型別,如果方法的接收者是值型別,那麼方法的實現者可以是值型別也可以是指向該型別的指標型別。
面試題一個,下面的程式碼能否編譯通過?
package main import ( "fmt" ) type People interface { Speak(string) string } type Stduent struct{} func (stu *Stduent) Speak(think string) (talk string) { if think == "bitch" { talk = "You are a good boy" } else { talk = "hi" } return } func main() { var peo People = Stduent{} think := "bitch" fmt.Println(peo.Speak(think)) }
答案:不能。
分析:首先Speak的方法的接收者是*Student , 根據上面的規則,那麼實現該方法的實現者只能是 *Student,但是 var peo People = Student{} 這裡卻將Student作為實現者賦值給了介面,這裡就會出現問題。Integer(25)是一個字面量,而字面量是一個常數,所以沒有辦法定址。
// Sample program to show how polymorphic behavior with interfaces. package main import ( "fmt" ) type notifier interface { notify() } // user defines a user in the program. type user struct { name string email string } func (u *user) notify() { fmt.Printf("Sending user email to %s<%s>n", u.name, u.email) } type admin struct { name string email string } func (a *admin) notify() { fmt.Printf("Sending admin email to %s<%s>n", a.name, a.email) } // main is the entry point for the application. func main() { // Create a user value and pass it to sendNotification. bill := user{"Bill", "bill@email.com"} sendNotification(&bill) // Create an admin value and pass it to sendNotification. lisa := admin{"Lisa", "lisa@email.com"} sendNotification(&lisa) } func sendNotification(n notifier) { n.notify() }
上面的程式碼很好的說明的介面的多型,user和admin都實現了notify方法,既實現了的notifier介面,sendNotification函數接收一個實現notifier介面的範例,從而user和admin都可以當作引數使用sendNotification函數,而sendNotification裡面的notify方法執行根據的是具體傳入的範例中實現的方法。
到此這篇關於Go語言介面的文章就介紹到這了。希望對大家的學習有所幫助,也希望大家多多支援it145.com。
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45