欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Go語(yǔ)言接口的用法詳解

 更新時(shí)間:2022年07月13日 14:44:14   作者:奮斗的大橙子  
本文詳細(xì)講解了Go語(yǔ)言接口的用法,文中通過(guò)示例代碼介紹的非常詳細(xì)。對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下

一、接口的定義和好處

我們都知道接口給類(lèi)提供了一種多態(tài)的機(jī)制,什么是多態(tài),多態(tài)就是系統(tǒng)根據(jù)類(lèi)型的具體實(shí)現(xiàn)完成不同的行為。

以下代碼簡(jiǎn)單說(shuō)明了接口的作用

package main

import (
    "fmt"
    "io"
    "net/http"
    "os"
)

// init 在main 函數(shù)之前調(diào)用
func init() {
    if len(os.Args) != 2 {
        fmt.Println("Usage: ./example2 <url>")
        os.Exit(-1)
    }
}

// main 是應(yīng)用程序的入口
func main() {
    // 從Web 服務(wù)器得到響應(yīng)
    r, err := http.Get(os.Args[1])
    if err != nil {
        fmt.Println(err)
        return
    }

    // 從Body 復(fù)制到Stdout
    io.Copy(os.Stdout, r.Body)
    if err := r.Body.Close(); err != nil {
        fmt.Println(err)
    }
}

①注意下 http.Get(os.Args[1]) 這里他的返回值r是一個(gè)Response對(duì)象的指針,也就是請(qǐng)求的結(jié)果

做過(guò)web開(kāi)發(fā)的都知道,下面是源代碼

func Get(url string) (resp *Response, err error) {
    return DefaultClient.Get(url)
}

以下是Response的結(jié)構(gòu),這里有一個(gè)Body,是一個(gè)io.ReadCloser類(lèi)型的,這是啥?往下看

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是一個(gè)接口哦!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) 這個(gè)方法,查看源碼如下

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

這里的輸入?yún)?shù)dst是一個(gè)實(shí)現(xiàn)了Writer接口的對(duì)象,而src則是一個(gè)實(shí)現(xiàn)了Reader接口的對(duì)象,由此,我們可以知道為什么io.Copy(os.Stdout, r.Body)的第二個(gè)參數(shù)可以傳r.Body了,因?yàn)棰僦姓故玖薆ody這個(gè)對(duì)象是實(shí)現(xiàn)了Reader接口的。同理os.Stdout對(duì)象這個(gè)接口值表示標(biāo)準(zhǔn)輸出設(shè)備,并且已經(jīng)實(shí)現(xiàn)了io.Writer 接口

os.Stdout返回的是一個(gè)*File, File里面只有一個(gè)*file,而*file是實(shí)現(xiàn)了下面兩個(gè)接口的,下面是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
}

所以說(shuō)*File本身是繼承了Writer和Reader接口的類(lèi)型。

綜上有了參數(shù)或者返回值是接口類(lèi)型,就不用關(guān)注于具體的返回類(lèi)型是什么,只要實(shí)現(xiàn)了的接口的方法都是可以被接受的。

二、接口值和實(shí)際對(duì)象值是怎么轉(zhuǎn)化和存儲(chǔ)的

我們都知道 如果一個(gè)類(lèi)型實(shí)現(xiàn)了某個(gè)接口,那么這個(gè)類(lèi)型的實(shí)際值是可以賦值給一個(gè)接口的變量的。

在C#中是這樣的,例如將一個(gè)List賦值給一個(gè)IEnumerable類(lèi)型的變量

IEnumerable<int> list = new List<int>(); 

在Go語(yǔ)言中也是這樣的,請(qǐng)看下面的代碼

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 是應(yīng)用程序的入口
func main() {

    bird1:= Bird{Name:"Big"}
    bird2:= new(Bird)
    bird2.Name = "Small"

    print(bird1)
    print(bird2)

    var eat1 eat
    eat1 = bird1
    print(eat1)
}

結(jié)果

Bird:Big eat

Bird:Small eat

Bird:Big eat

這里定義了一個(gè)eat接口,有一個(gè)Bird的類(lèi)型實(shí)現(xiàn)了該接口,print函數(shù)接受一個(gè)eat接口類(lèi)型的參數(shù),

這里可以看到前兩次直接把bird1和bird2作為參數(shù)傳入到print函數(shù)內(nèi),第二次則是聲明了一個(gè)eat接口類(lèi)型的變量eat1,然后將bird1進(jìn)行了賦值。換句話(huà)說(shuō)接口類(lèi)型變量實(shí)際承載了實(shí)際類(lèi)型值。這里是如何承載的呢?

這里我們把 eat1 稱(chēng)作 接口值,將bird1稱(chēng)作實(shí)體類(lèi)型值,eat1和bird1的關(guān)系如下:

接口值可以看成兩部分組合(都是指針)而成的。第一部分是【iTable的地址】第二部分是【實(shí)體類(lèi)型值的地址】

關(guān)于interface的解析:

http://www.dbjr.com.cn/article/255284.htm

三、方法集的概念

簡(jiǎn)單的講:方法集定義了接口的接受規(guī)則

舉例說(shuō)明:

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)

}

如上代碼,定義了一個(gè)notifier接口,有一個(gè)方法nitify()方法,定義了一個(gè)user類(lèi)型的結(jié)構(gòu),實(shí)現(xiàn)了notify方法,接收者類(lèi)型是user,即實(shí)現(xiàn)了notifier接口,又定義了一個(gè)sendNotification方法,接收一個(gè)實(shí)現(xiàn)notifier接口的類(lèi)型,并調(diào)用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)
}

現(xiàn)在修正一下代碼,將接收者改為user的指針類(lèi)型。此時(shí)會(huì)發(fā)現(xiàn)原來(lái)調(diào)用的地方會(huì)出現(xiàn)錯(cuò)誤。

cannot use u (type user) as type notifier in argument to sendNotification:user does not implement notifier (notify method has pointer receiver)

不能將u(類(lèi)型是user)作為sendNotification 的參數(shù)類(lèi)型notifier:user 類(lèi)型并沒(méi)有實(shí)現(xiàn)notifier(notify 方法使用指針接收者聲明)

為什么會(huì)出現(xiàn)上面的問(wèn)題?要了解用指針接收者來(lái)實(shí)現(xiàn)接口時(shí)為什么user 類(lèi)型的值無(wú)法實(shí)現(xiàn)該接口,需要先了解方法集。方法集定義了一組關(guān)聯(lián)到給定類(lèi)型的值或者指針的方法。

定義方法時(shí)使用的接收者的類(lèi)型決定了這個(gè)方法是關(guān)聯(lián)到值,還是關(guān)聯(lián)到指針,還是兩個(gè)都關(guān)聯(lián)。

以下是Go語(yǔ)言規(guī)范中的方法集:

上表的意思是:類(lèi)型的值只能實(shí)現(xiàn)值接收者的接口;指向類(lèi)型的指針,既可以實(shí)現(xiàn)值接收者的接口,也可以實(shí)現(xiàn)指針接收者的接口。

從接收者的角度來(lái)看一下這些規(guī)則

如果是值接收者,實(shí)體類(lèi)型的值和指針都可以實(shí)現(xiàn)對(duì)應(yīng)的接口;如果是指針接收者,那么只有類(lèi)型的指針能夠?qū)崿F(xiàn)對(duì)應(yīng)的接口。

所以針對(duì)上面的問(wèn)題,將傳入的u變成傳入地址就可以了(可以套用一下表格,接收者*user對(duì)應(yīng)的values是*user,所以傳地址對(duì)應(yīng)上面表格淺藍(lán)色部分)

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)

}

綜上我們總結(jié)一下,也就是說(shuō)如果你的方法的接受者的類(lèi)型是指針類(lèi)型,那么方法的實(shí)現(xiàn)者就只能是指向該類(lèi)型的指針類(lèi)型,如果方法的接收者是值類(lèi)型,那么方法的實(shí)現(xiàn)者可以是值類(lèi)型也可以是指向該類(lèi)型的指針類(lèi)型。

面試題一個(gè),下面的代碼能否編譯通過(guò)?

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 , 根據(jù)上面的規(guī)則,那么實(shí)現(xiàn)該方法的實(shí)現(xiàn)者只能是 *Student,但是 var peo People = Student{} 這里卻將Student作為實(shí)現(xiàn)者賦值給了接口,這里就會(huì)出現(xiàn)問(wèn)題。Integer(25)是一個(gè)字面量,而字面量是一個(gè)常量,所以沒(méi)有辦法尋址。

四、多態(tài)

// 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()
}

上面的代碼很好的說(shuō)明的接口的多態(tài),user和admin都實(shí)現(xiàn)了notify方法,既實(shí)現(xiàn)了的notifier接口,sendNotification函數(shù)接收一個(gè)實(shí)現(xiàn)notifier接口的實(shí)例,從而user和admin都可以當(dāng)作參數(shù)使用sendNotification函數(shù),而sendNotification里面的notify方法執(zhí)行根據(jù)的是具體傳入的實(shí)例中實(shí)現(xiàn)的方法。

到此這篇關(guān)于Go語(yǔ)言接口的文章就介紹到這了。希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

  • golang 實(shí)現(xiàn)兩個(gè)結(jié)構(gòu)體復(fù)制字段

    golang 實(shí)現(xiàn)兩個(gè)結(jié)構(gòu)體復(fù)制字段

    這篇文章主要介紹了golang 實(shí)現(xiàn)兩個(gè)結(jié)構(gòu)體復(fù)制字段,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2021-04-04
  • golang的HTTP基本認(rèn)證機(jī)制實(shí)例詳解

    golang的HTTP基本認(rèn)證機(jī)制實(shí)例詳解

    這篇文章主要介紹了golang的HTTP基本認(rèn)證機(jī)制,結(jié)合實(shí)例形式較為詳細(xì)的分析了HTTP請(qǐng)求響應(yīng)的過(guò)程及認(rèn)證機(jī)制實(shí)現(xiàn)技巧,需要的朋友可以參考下
    2016-07-07
  • Go目錄文件路徑操作的實(shí)現(xiàn)

    Go目錄文件路徑操作的實(shí)現(xiàn)

    在Go語(yǔ)言中,可以使用絕對(duì)路徑或相對(duì)路徑來(lái)表示文件路徑,本文就來(lái)介紹一下Go目錄文件路徑操作,感興趣的可以了解一下
    2023-10-10
  • Golang中的Interface詳解

    Golang中的Interface詳解

    本文詳細(xì)講解了Golang中的Interface,文中通過(guò)示例代碼介紹的非常詳細(xì)。對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2022-07-07
  • golang?gorm框架數(shù)據(jù)庫(kù)的連接操作示例

    golang?gorm框架數(shù)據(jù)庫(kù)的連接操作示例

    這篇文章主要為大家介紹了golang?gorm框架數(shù)據(jù)庫(kù)操作示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步早日升職加薪
    2022-04-04
  • Go語(yǔ)言中字符串拼接的方法總結(jié)

    Go語(yǔ)言中字符串拼接的方法總結(jié)

    在Go語(yǔ)言中,我們可以使用+操作符、bytes.Buffer、strings.Builder等方法來(lái)拼接字符串,本文主要為大家介紹了這些常用方法的實(shí)現(xiàn)以及性能對(duì)比,感興趣的小伙伴可以了解下
    2023-11-11
  • go語(yǔ)言變量定義用法實(shí)例

    go語(yǔ)言變量定義用法實(shí)例

    這篇文章主要介紹了go語(yǔ)言變量定義用法,實(shí)例分析了go語(yǔ)言變量的定義及使用技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下
    2015-02-02
  • 簡(jiǎn)單聊聊Go語(yǔ)言的注釋

    簡(jiǎn)單聊聊Go語(yǔ)言的注釋

    這篇文章主要介紹了簡(jiǎn)單聊聊Go語(yǔ)言的注釋的相關(guān)資料,需要的朋友可以參考下
    2023-08-08
  • 詳解prometheus監(jiān)控golang服務(wù)實(shí)踐記錄

    詳解prometheus監(jiān)控golang服務(wù)實(shí)踐記錄

    這篇文章主要介紹了詳解prometheus監(jiān)控golang服務(wù)實(shí)踐記錄,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-11-11
  • golang中new與make的區(qū)別講解

    golang中new與make的區(qū)別講解

    new只能開(kāi)辟單個(gè)空間,不能為引用類(lèi)型開(kāi)辟多個(gè)空間,并且new是對(duì)類(lèi)型進(jìn)行內(nèi)存的開(kāi)辟,返回一個(gè)指向該內(nèi)存空間的指針類(lèi)型,如果使用new去初始化引用數(shù)據(jù)類(lèi)型,不是很合適(當(dāng)然,new一個(gè)對(duì)象還是可以的),因此就需要用到另一個(gè)內(nèi)置函數(shù)make,需要的朋友可以參考下
    2023-01-01

最新評(píng)論