GO語言筆記--接口

1.接口interface

1)接口是一個或多個方法簽名的集合

2)只要某個類型擁有該接口的所有方法簽名,即算實現該接口無需顯示聲明實現了哪個接口,這稱爲Structural Typing

3)接口只有方法聲明,沒有實現,沒有數據字段

4)接口可以匿名嵌入其他接口,或嵌入到結構中

5)將對象賦值給接口時,會發生拷貝,而接口內部存儲的是指向這個複製品的指針,既無法修改複製品的狀態,也無法獲取指針

6)只有當接口存儲的類型和對象爲nil時,接口才等於nil

7)接口調用不會做receiver的自動轉換

8)接口統一支持匿名自動方法

9)接口也可以實現類似OOP中的多態

10)空接口可以作爲任何類型數據的容器

11)接口是由使用者定義得

 

2.類型斷言

1)通過類型斷言的ok pattern可以判斷接口中的數據類型

2)使用tupe switch 則可針對空接口進行比較全面的類型判斷

3.接口轉換

可以將擁有超集的接口轉換爲子集的接口

實例,使用者定義接口

創建一個retiever目錄,裏面包含main.go 以及csdn/csdnRetiever.go兩個文件

main.go代碼,這裏面使用到了定義的csdn這個包調用接口Retiever的Get方法:

package main


import (
    "fmt"
    "my-code/test-code/retiever/csdn"
)


type Retiever interface {
    Get(url string) string
}


func download(r Retiever) string {
    return r.Get("www.csdn.com")
}


func main() {
    var r Retiever
    r = csdn.Retiever{"hello www.csdn.com"}
    fmt.Println(download(r))
}

csdn/csdnRetiever.go代碼中包含接口定義:

package csdn


type Retiever struct {
    Contents string
}


func (r Retiever) Get(url string) string {
    return r.Contents
}

上述代碼傳入一個url字符串並直接return
輸出:
API server listening at: 127.0.0.1:34276
hello www.csdn.com

實例,訪問百度

創建一個retiever目錄,裏面包含main.go 以及baidu/baidu.go兩個文件

main.go代碼,這裏面使用到了定義的baidu這個包調用接口Retiever的Get方法:

package main


import (
    "fmt"
    "my-code/test-code/retiever/baidu"
)


type Retiever interface {
    Get(url string) string
}


func download(r Retiever) string {
    return r.Get("http://www.baidu.com")
}


func main() {
    var r Retiever
    r = baidu.Retiever{}
    fmt.Println(download(r))
}

baidu/baidu.go代碼直接調用內置http接口訪問百度:

package baidu


import (
    "net/http"
    "net/http/httputil"
    "time"
)


type Retiever struct {
    UserAgent string
    TimeOut   time.Duration
}


func (r Retiever) Get(url string) string {
    resp, err := http.Get(url)
    if err != nil {
        panic(err)
    }
    result, err := httputil.DumpResponse(resp, true)
    resp.Body.Close()
    if err != nil {
        panic(err)
    }
    return string(result)
}

上述實例運行結果是獲取到了百度首頁的html源碼

4.查看接口內部元素

接口內部究竟是什麼內容呢,將上述main.go代碼修改如下打印出來查看:

func main() {
    var r Retiever
    r = baidu.Retiever{}
    //fmt.Println(download(r))
    fmt.Printf("%T\n%v\n", r, r)
}
運行結果如下:
API server listening at: 127.0.0.1:18265
baidu.Retiever
{ 0s}

上述打印看出%T打印的內容是baidu.Retiever這是接口名稱,%v的內容是{ 0s},這是因爲裏面兩個元素UserAgent未賦值因此是個空格,TimeOut未賦值所以默認0秒。爲了更清晰的看出效果,下面將上述元素賦值查看:

package main


import (
    "fmt"
    "my-code/retiever/baidu"
    "time"
)


type Retiever interface {
    Get(url string) string
}


func download(r Retiever) string {
    return r.Get("http://www.baidu.com")
}


func main() {
    var r Retiever
    r = baidu.Retiever{
        UserAgent: "Mozilla/5.0",
        TimeOut:   time.Minute,
    }
    //fmt.Println(download(r))
    fmt.Printf("%T\n%v\n", r, r)
}

運行效果如下:
API server listening at: 127.0.0.1:16843
baidu.Retiever
{Mozilla/5.0 1m0s}

可以明顯看出被賦值了

5.指針類型

如果接口類型修改爲指針類型呢,代碼如下:

baidu/baidu.go

package baidu


import (
    "net/http"
    "net/http/httputil"
    "time"
)


type Retiever struct {
    UserAgent string
    TimeOut   time.Duration
}


func (r *Retiever) Get(url string) string {
    resp, err := http.Get(url)
    if err != nil {
        panic(err)
    }
    result, err := httputil.DumpResponse(resp, true)
    resp.Body.Close()
    if err != nil {
        panic(err)
    }
    return string(result)
}

main.go源碼不變:

package main


import (
    "fmt"
    "my-code/retiever/baidu"
    "time"
)


type Retiever interface {
    Get(url string) string
}


func download(r Retiever) string {
    return r.Get("http://www.baidu.com")
}


func main() {
    var r Retiever
    r = baidu.Retiever{
        UserAgent: "Mozilla/5.0",
        TimeOut:   time.Minute,
    }
    //fmt.Println(download(r))
    fmt.Printf("%T\n%v\n", r, r)
}

那麼上述就會出錯,執行報錯如下:

# my-code/retiever

.\main.go:19:4: cannot use baidu.Retiever literal (type baidu.Retiever) as type Retiever in assignment:

baidu.Retiever does not implement Retiever (Get method has pointer receiver)

exit status 2

Process exiting with code: 1

此時就要修改爲取地址了:

主函數修改如下:

func main() {
    var r Retiever
    r = &baidu.Retiever{
        UserAgent: "Mozilla/5.0",
        TimeOut:   time.Minute,
    }
    //fmt.Println(download(r))
    fmt.Printf("%T\n%v\n", r, r)
}

這樣執行就不會出錯了。

Type assertion
通過指針類型獲取內部元素:
val := r.(*baidu.Retiever)
fmt.Println(val.TimeOut)
這樣就可以取得具體元素了。
 

6.接口的組合

baidu/baidu.go提供Retiever接口:

package baidu


type Retiever struct {
    Contents string
}


func (r *Retiever) Post(url string, form map[string]string) string {
    r.Contents = form["contents"]
    return "ok"
}


func (r *Retiever) Get(url string) string {
    return r.Contents
}

 main.go代碼這裏面使用Poster和Retiever兩個接口組合成一個RetieverPoster接口

package main


import (
    "fmt"
    "my-code/retiever/baidu"
)


type Retiever interface {
    Get(url string) string
}


type Poster interface {
    Post(url string, form map[string]string) string
}


const url = "http://www.baidu.com"


func download(r Retiever) string {
    return r.Get(url)
}


func post(poster Poster) {
    poster.Post(url,
        map[string]string{
            "name":   "baidu",
            "course": "golang",
        })
}


type RetieverPoster interface {
    Retiever
    Poster
}


func session(s RetieverPoster) string {
    s.Post(url, map[string]string{
        "contents": "hello baidu",
    })
    return s.Get(url)
}


func main() {
    retriever := baidu.Retiever{"hello http://www.baidu.com"}
    fmt.Println(session(&retriever))
}
執行效果如下:
API server listening at: 127.0.0.1:41871
hello baidu

雖然傳入的是"hello http://www.baidu.com"但是輸出的卻是"hello baidu"這是因爲在session方法裏面修改了內容導致的。

7.GO語言常用系統接口

Stringer接口,在fmt/print.go內:

type Stringer interface {

    String() string

}

應用:在baidu包內實現一個String(),代碼如下:

baidu/baidu.go

package baidu


import "fmt"


type Retiever struct {
    Contents string
}


func (r *Retiever) String() string {
    return fmt.Sprintf(
        "Retriver:{Contents=%s}", r.Contents)
}
func (r *Retiever) Post(url string, form map[string]string) string {
    r.Contents = form["contents"]
    return "ok"
}


func (r *Retiever) Get(url string) string {
    return r.Contents
}

main.go

package main


import (
    "fmt"
    "my-code/retiever/baidu"
)


type Retiever interface {
    Get(url string) string
}


func main() {
    r := &baidu.Retiever{
        Contents: "hello word",
    }
    fmt.Printf("%T\n%v\n", r, r)
}

輸出:
API server listening at: 127.0.0.1:39863
*baidu.Retiever
Retriver:{Contents=hello word}

此外還有兩個接口在io.go包內,分別是Reader和Writer

type Reader interface {

    Read(p []byte) (n int, err error)

}

type Writer interface {

    Write(p []byte) (n int, err error)

}

實例:

package main


import (
    "bufio"
    "fmt"
    "io"
    "os"
    "strings"
)


func printFile(name string) {
    file, err := os.Open(name)
    if err != nil {
        panic(err)
    }
    testIoReader(file)
}


func testIoReader(reader io.Reader) {
    scanner := bufio.NewScanner(reader)
    for scanner.Scan() {
        fmt.Println(scanner.Text())
    }
}


func main() {
    printFile("ioTest.go")
    fmt.Println("##############print string#################")
    s := `abc"d"
    beijing
    nihao
    hello`
    testIoReader(strings.NewReader(s))
}

可以打印文件內容或者字符串裏面的內容。

小結:

1.接口變量裏面有什麼
1)接口變量自帶指針
2)接口變量同樣採用值傳遞,幾乎不需要使用接口的指針
3)指針接收者實現只能以指針方式使用;值接受者都可
 

2.查看接口變量

1)表示任何類型:interface{}

2)Type Assertion

3)Type Switch

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章