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

Go語言interface詳解

 更新時(shí)間:2014年10月29日 09:46:07   投稿:junjie  
這篇文章主要介紹了Go語言interface詳解,本文講解了什么是interface、interface類型、interface值、空interface、interface函數(shù)參數(shù)等內(nèi)容,需要的朋友可以參考下

interface

Go語言里面設(shè)計(jì)最精妙的應(yīng)該算interface,它讓面向?qū)ο?,?nèi)容組織實(shí)現(xiàn)非常的方便,當(dāng)你看完這一章,你就會被interface的巧妙設(shè)計(jì)所折服。

什么是interface

簡單的說,interface是一組method的組合,我們通過interface來定義對象的一組行為。

我們前面一章最后一個(gè)例子中Student和Employee都能SayHi,雖然他們的內(nèi)部實(shí)現(xiàn)不一樣,但是那不重要,重要的是他們都能say hi

讓我們來繼續(xù)做更多的擴(kuò)展,Student和Employee實(shí)現(xiàn)另一個(gè)方法Sing,然后Student實(shí)現(xiàn)方法BorrowMoney而Employee實(shí)現(xiàn)SpendSalary。

這樣Student實(shí)現(xiàn)了三個(gè)方法:SayHi、Sing、BorrowMoney;而Employee實(shí)現(xiàn)了SayHi、Sing、SpendSalary。

上面這些方法的組合稱為interface(被對象Student和Employee實(shí)現(xiàn))。例如Student和Employee都實(shí)現(xiàn)了interface:SayHi和Sing,也就是這兩個(gè)對象是該interface類型。而Employee沒有實(shí)現(xiàn)這個(gè)interface:SayHi、Sing和BorrowMoney,因?yàn)镋mployee沒有實(shí)現(xiàn)BorrowMoney這個(gè)方法。

interface類型

interface類型定義了一組方法,如果某個(gè)對象實(shí)現(xiàn)了某個(gè)接口的所有方法,則此對象就實(shí)現(xiàn)了此接口。詳細(xì)的語法參考下面這個(gè)例子

復(fù)制代碼 代碼如下:

type Human struct {
    name string
    age int
    phone string
}

type Student struct {
    Human //匿名字段Human
    school string
    loan float32
}

type Employee struct {
    Human //匿名字段Human
    company string
    money float32
}

//Human對象實(shí)現(xiàn)Sayhi方法
func (h *Human) SayHi() {
    fmt.Printf("Hi, I am %s you can call me on %s\n", h.name, h.phone)
}

// Human對象實(shí)現(xiàn)Sing方法
func (h *Human) Sing(lyrics string) {
    fmt.Println("La la, la la la, la la la la la...", lyrics)
}

//Human對象實(shí)現(xiàn)Guzzle方法
func (h *Human) Guzzle(beerStein string) {
    fmt.Println("Guzzle Guzzle Guzzle...", beerStein)
}

// Employee重載Human的Sayhi方法
func (e *Employee) SayHi() {
    fmt.Printf("Hi, I am %s, I work at %s. Call me on %s\n", e.name,
        e.company, e.phone) //此句可以分成多行
}

//Student實(shí)現(xiàn)BorrowMoney方法
func (s *Student) BorrowMoney(amount float32) {
    s.loan += amount // (again and again and...)
}

//Employee實(shí)現(xiàn)SpendSalary方法
func (e *Employee) SpendSalary(amount float32) {
    e.money -= amount // More vodka please!!! Get me through the day!
}

// 定義interface
type Men interface {
    SayHi()
    Sing(lyrics string)
    Guzzle(beerStein string)
}

type YoungChap interface {
    SayHi()
    Sing(song string)
    BorrowMoney(amount float32)
}

type ElderlyGent interface {
    SayHi()
    Sing(song string)
    SpendSalary(amount float32)
}

通過上面的代碼我們可以知道,interface可以被任意的對象實(shí)現(xiàn)。我們看到上面的Men interface被Human、Student和Employee實(shí)現(xiàn)。同理,一個(gè)對象可以實(shí)現(xiàn)任意多個(gè)interface,例如上面的Student實(shí)現(xiàn)了Men和YoungChap兩個(gè)interface。

最后,任意的類型都實(shí)現(xiàn)了空interface(我們這樣定義:interface{}),也就是包含0個(gè)method的interface。

interface值

那么interface里面到底能存什么值呢?如果我們定義了一個(gè)interface的變量,那么這個(gè)變量里面可以存實(shí)現(xiàn)這個(gè)interface的任意類型的對象。例如上面例子中,我們定義了一個(gè)Men interface類型的變量m,那么m里面可以存Human、Student或者Employee值。

因?yàn)閙能夠持有這三種類型的對象,所以我們可以定義一個(gè)包含Men類型元素的slice,這個(gè)slice可以被賦予實(shí)現(xiàn)了Men接口的任意結(jié)構(gòu)的對象,這個(gè)和我們傳統(tǒng)意義上面的slice有所不同。

讓我們來看一下下面這個(gè)例子:

復(fù)制代碼 代碼如下:

package main
import "fmt"

type Human struct {
    name string
    age int
    phone string
}

type Student struct {
    Human //匿名字段
    school string
    loan float32
}

type Employee struct {
    Human //匿名字段
    company string
    money float32
}

//Human實(shí)現(xiàn)SayHi方法
func (h Human) SayHi() {
    fmt.Printf("Hi, I am %s you can call me on %s\n", h.name, h.phone)
}

//Human實(shí)現(xiàn)Sing方法
func (h Human) Sing(lyrics string) {
    fmt.Println("La la la la...", lyrics)
}

//Employee重載Human的SayHi方法
func (e Employee) SayHi() {
    fmt.Printf("Hi, I am %s, I work at %s. Call me on %s\n", e.name,
        e.company, e.phone)
    }

// Interface Men被Human,Student和Employee實(shí)現(xiàn)
// 因?yàn)檫@三個(gè)類型都實(shí)現(xiàn)了這兩個(gè)方法
type Men interface {
    SayHi()
    Sing(lyrics string)
}

func main() {
    mike := Student{Human{"Mike", 25, "222-222-XXX"}, "MIT", 0.00}
    paul := Student{Human{"Paul", 26, "111-222-XXX"}, "Harvard", 100}
    sam := Employee{Human{"Sam", 36, "444-222-XXX"}, "Golang Inc.", 1000}
    Tom := Employee{Human{"Tom", 37, "222-444-XXX"}, "Things Ltd.", 5000}

    //定義Men類型的變量i
    var i Men

    //i能存儲Student
    i = mike
    fmt.Println("This is Mike, a Student:")
    i.SayHi()
    i.Sing("November rain")

    //i也能存儲Employee
    i = Tom
    fmt.Println("This is Tom, an Employee:")
    i.SayHi()
    i.Sing("Born to be wild")

    //定義了slice Men
    fmt.Println("Let's use a slice of Men and see what happens")
    x := make([]Men, 3)
    //這三個(gè)都是不同類型的元素,但是他們實(shí)現(xiàn)了interface同一個(gè)接口
    x[0], x[1], x[2] = paul, sam, mike

    for _, value := range x{
        value.SayHi()
    }
}

通過上面的代碼,你會發(fā)現(xiàn)interface就是一組抽象方法的集合,它必須由其他非interface類型實(shí)現(xiàn),而不能自我實(shí)現(xiàn), Go通過interface實(shí)現(xiàn)了duck-typing:即"當(dāng)看到一只鳥走起來像鴨子、游泳起來像鴨子、叫起來也像鴨子,那么這只鳥就可以被稱為鴨子"。

空interface

空interface(interface{})不包含任何的method,正因?yàn)槿绱?,所有的類型都?shí)現(xiàn)了空interface??読nterface對于描述起不到任何的作用(因?yàn)樗话魏蔚膍ethod),但是空interface在我們需要存儲任意類型的數(shù)值的時(shí)候相當(dāng)有用,因?yàn)樗梢源鎯θ我忸愋偷臄?shù)值。它有點(diǎn)類似于C語言的void*類型。

復(fù)制代碼 代碼如下:

// 定義a為空接口
var a interface{}
var i int = 5
s := "Hello world"
// a可以存儲任意類型的數(shù)值
a = i
a = s

一個(gè)函數(shù)把interface{}作為參數(shù),那么他可以接受任意類型的值作為參數(shù),如果一個(gè)函數(shù)返回interface{},那么也就可以返回任意類型的值。是不是很有用啊!

interface函數(shù)參數(shù)

interface的變量可以持有任意實(shí)現(xiàn)該interface類型的對象,這給我們編寫函數(shù)(包括method)提供了一些額外的思考,我們是不是可以通過定義interface參數(shù),讓函數(shù)接受各種類型的參數(shù)。

舉個(gè)例子:fmt.Println是我們常用的一個(gè)函數(shù),但是你是否注意到它可以接受任意類型的數(shù)據(jù)。打開fmt的源碼文件,你會看到這樣一個(gè)定義:

復(fù)制代碼 代碼如下:

type Stringer interface {
     String() string
}

也就是說,任何實(shí)現(xiàn)了String方法的類型都能作為參數(shù)被fmt.Println調(diào)用,讓我們來試一試
復(fù)制代碼 代碼如下:

package main
import (
    "fmt"
    "strconv"
)

type Human struct {
    name string
    age int
    phone string
}

// 通過這個(gè)方法 Human 實(shí)現(xiàn)了 fmt.Stringer
func (h Human) String() string {
    return "❰"+h.name+" - "+strconv.Itoa(h.age)+" years -  ✆ " +h.phone+"❱"
}

func main() {
    Bob := Human{"Bob", 39, "000-7777-XXX"}
    fmt.Println("This Human is : ", Bob)
}

現(xiàn)在我們再回顧一下前面的Box示例,你會發(fā)現(xiàn)Color結(jié)構(gòu)也定義了一個(gè)method:String。其實(shí)這也是實(shí)現(xiàn)了fmt.Stringer這個(gè)interface,即如果需要某個(gè)類型能被fmt包以特殊的格式輸出,你就必須實(shí)現(xiàn)Stringer這個(gè)接口。如果沒有實(shí)現(xiàn)這個(gè)接口,fmt將以默認(rèn)的方式輸出。

復(fù)制代碼 代碼如下:

//實(shí)現(xiàn)同樣的功能
fmt.Println("The biggest one is", boxes.BiggestsColor().String())
fmt.Println("The biggest one is", boxes.BiggestsColor())

注:實(shí)現(xiàn)了error接口的對象(即實(shí)現(xiàn)了Error() string的對象),使用fmt輸出時(shí),會調(diào)用Error()方法,因此不必再定義String()方法了。

interface變量存儲的類型

我們知道interface的變量里面可以存儲任意類型的數(shù)值(該類型實(shí)現(xiàn)了interface)。那么我們怎么反向知道這個(gè)變量里面實(shí)際保存了的是哪個(gè)類型的對象呢?目前常用的有兩種方法:

Comma-ok斷言

Go語言里面有一個(gè)語法,可以直接判斷是否是該類型的變量: value, ok = element.(T),這里value就是變量的值,ok是一個(gè)bool類型,element是interface變量,T是斷言的類型。

如果element里面確實(shí)存儲了T類型的數(shù)值,那么ok返回true,否則返回false。

讓我們通過一個(gè)例子來更加深入的理解。

復(fù)制代碼 代碼如下:

package main

import (
    "fmt"
    "strconv"
)

type Element interface{}
type List [] Element

type Person struct {
    name string
    age int
}

//定義了String方法,實(shí)現(xiàn)了fmt.Stringer
func (p Person) String() string {
    return "(name: " + p.name + " - age: "+strconv.Itoa(p.age)+ " years)"
}

func main() {
    list := make(List, 3)
    list[0] = 1 // an int
    list[1] = "Hello" // a string
    list[2] = Person{"Dennis", 70}

    for index, element := range list {
        if value, ok := element.(int); ok {
            fmt.Printf("list[%d] is an int and its value is %d\n", index, value)
        } else if value, ok := element.(string); ok {
            fmt.Printf("list[%d] is a string and its value is %s\n", index, value)
        } else if value, ok := element.(Person); ok {
            fmt.Printf("list[%d] is a Person and its value is %s\n", index, value)
        } else {
            fmt.Println("list[%d] is of a different type", index)
        }
    }
}

是不是很簡單啊,同時(shí)你是否注意到了多個(gè)if里面,還記得我前面介紹流程時(shí)講過,if里面允許初始化變量。

也許你注意到了,我們斷言的類型越多,那么if else也就越多,所以才引出了下面要介紹的switch。

switch測試

最好的講解就是代碼例子,現(xiàn)在讓我們重寫上面的這個(gè)實(shí)現(xiàn)

復(fù)制代碼 代碼如下:

package main

import (
    "fmt"
    "strconv"
)

type Element interface{}
type List [] Element

type Person struct {
    name string
    age int
}

//打印
func (p Person) String() string {
    return "(name: " + p.name + " - age: "+strconv.Itoa(p.age)+ " years)"
}

func main() {
    list := make(List, 3)
    list[0] = 1 //an int
    list[1] = "Hello" //a string
    list[2] = Person{"Dennis", 70}

    for index, element := range list{
        switch value := element.(type) {
            case int:
                fmt.Printf("list[%d] is an int and its value is %d\n", index, value)
            case string:
                fmt.Printf("list[%d] is a string and its value is %s\n", index, value)
            case Person:
                fmt.Printf("list[%d] is a Person and its value is %s\n", index, value)
            default:
                fmt.Println("list[%d] is of a different type", index)
        }
    }
}

這里有一點(diǎn)需要強(qiáng)調(diào)的是:element.(type)語法不能在switch外的任何邏輯里面使用,如果你要在switch外面判斷一個(gè)類型就使用comma-ok。

嵌入interface

Go里面真正吸引人的是它內(nèi)置的邏輯語法,就像我們在學(xué)習(xí)Struct時(shí)學(xué)習(xí)的匿名字段,多么的優(yōu)雅啊,那么相同的邏輯引入到interface里面,那不是更加完美了。如果一個(gè)interface1作為interface2的一個(gè)嵌入字段,那么interface2隱式的包含了interface1里面的method。

我們可以看到源碼包c(diǎn)ontainer/heap里面有這樣的一個(gè)定義

復(fù)制代碼 代碼如下:

type Interface interface {
    sort.Interface //嵌入字段sort.Interface
    Push(x interface{}) //a Push method to push elements into the heap
    Pop() interface{} //a Pop elements that pops elements from the heap
}

我們看到sort.Interface其實(shí)就是嵌入字段,把sort.Interface的所有method給隱式的包含進(jìn)來了。也就是下面三個(gè)方法:

復(fù)制代碼 代碼如下:

type Interface interface {
    // Len is the number of elements in the collection.
    Len() int
    // Less returns whether the element with index i should sort
    // before the element with index j.
    Less(i, j int) bool
    // Swap swaps the elements with indexes i and j.
    Swap(i, j int)
}

另一個(gè)例子就是io包下面的 io.ReadWriter ,它包含了io包下面的Reader和Writer兩個(gè)interface:

復(fù)制代碼 代碼如下:

// io.ReadWriter
type ReadWriter interface {
    Reader
    Writer
}

反射

Go語言實(shí)現(xiàn)了反射,所謂反射就是能檢查程序在運(yùn)行時(shí)的狀態(tài)。我們一般用到的包是reflect包。如何運(yùn)用reflect包,官方的這篇文章詳細(xì)的講解了reflect包的實(shí)現(xiàn)原理,laws of reflection

使用reflect一般分成三步,下面簡要的講解一下:要去反射是一個(gè)類型的值(這些值都實(shí)現(xiàn)了空interface),首先需要把它轉(zhuǎn)化成reflect對象(reflect.Type或者reflect.Value,根據(jù)不同的情況調(diào)用不同的函數(shù))。這兩種獲取方式如下:

復(fù)制代碼 代碼如下:

t := reflect.TypeOf(i)    //得到類型的元數(shù)據(jù),通過t我們能獲取類型定義里面的所有元素
v := reflect.ValueOf(i)   //得到實(shí)際的值,通過v我們獲取存儲在里面的值,還可以去改變值

轉(zhuǎn)化為reflect對象之后我們就可以進(jìn)行一些操作了,也就是將reflect對象轉(zhuǎn)化成相應(yīng)的值,例如
復(fù)制代碼 代碼如下:

tag := t.Elem().Field(0).Tag  //獲取定義在struct里面的標(biāo)簽
name := v.Elem().Field(0).String()  //獲取存儲在第一個(gè)字段里面的值

獲取反射值能返回相應(yīng)的類型和數(shù)值
復(fù)制代碼 代碼如下:

var x float64 = 3.4
v := reflect.ValueOf(x)
fmt.Println("type:", v.Type())
fmt.Println("kind is float64:", v.Kind() == reflect.Float64)
fmt.Println("value:", v.Float())

最后,反射的話,那么反射的字段必須是可修改的,我們前面學(xué)習(xí)過傳值和傳引用,這個(gè)里面也是一樣的道理。反射的字段必須是可讀寫的意思是,如果下面這樣寫,那么會發(fā)生錯誤

復(fù)制代碼 代碼如下:

var x float64 = 3.4
v := reflect.ValueOf(x)
v.SetFloat(7.1)

如果要修改相應(yīng)的值,必須這樣寫
復(fù)制代碼 代碼如下:

var x float64 = 3.4
p := reflect.ValueOf(&x)
v := p.Elem()
v.SetFloat(7.1)

上面只是對反射的簡單介紹,更深入的理解還需要自己在編程中不斷的實(shí)踐。

相關(guān)文章

  • golang如何實(shí)現(xiàn)proxy代理簡單方法

    golang如何實(shí)現(xiàn)proxy代理簡單方法

    這篇文章主要給大家介紹了關(guān)于golang如何實(shí)現(xiàn)proxy代理簡單方法的相關(guān)資料,Proxy是golang實(shí)現(xiàn)的高性能http,https,websocket,tcp,udp,socks5,ss代理服務(wù)器,文中通過代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2023-10-10
  • go語言的初始化順序,包,變量,init詳解

    go語言的初始化順序,包,變量,init詳解

    這篇文章主要介紹了go語言的初始化順序,包,變量,init詳解,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2020-12-12
  • Go利用GJSON組件解鎖JSON讀取新姿勢

    Go利用GJSON組件解鎖JSON讀取新姿勢

    Go 標(biāo)準(zhǔn)庫提供了 encoding/json 包用于處理 json 數(shù)據(jù),同時(shí)第三方庫 GJSON & SJSON 也在 json 處理方面表現(xiàn)出色,下面我們就來看看如何使用GJSON解鎖JSON讀取新方法吧
    2025-03-03
  • Go語言中使用 buffered channel 實(shí)現(xiàn)線程安全的 pool

    Go語言中使用 buffered channel 實(shí)現(xiàn)線程安全的 pool

    這篇文章主要介紹了Go語言中使用 buffered channel 實(shí)現(xiàn)線程安全的 pool,因?yàn)镚o語言自帶的sync.Pool并不是很好用,所以自己實(shí)現(xiàn)了一線程安全的 pool,需要的朋友可以參考下
    2014-10-10
  • Golang解析yaml文件操作指南

    Golang解析yaml文件操作指南

    之前一直從事java開發(fā),習(xí)慣了使用yaml文件的格式,尤其是清晰的層次結(jié)構(gòu)、注釋,下面這篇文章主要給大家介紹了關(guān)于Golang解析yaml文件的相關(guān)資料,文中通過實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2022-09-09
  • golang數(shù)組和切片作為參數(shù)和返回值的實(shí)現(xiàn)

    golang數(shù)組和切片作為參數(shù)和返回值的實(shí)現(xiàn)

    本文主要介紹了golang數(shù)組和切片作為參數(shù)和返回值的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-02-02
  • go語言實(shí)現(xiàn)抓取高清圖片

    go語言實(shí)現(xiàn)抓取高清圖片

    本文給大家分享的是使用go語言實(shí)現(xiàn)的抓取高清美女圖片的代碼,原理非常簡單,這里就不多廢話了,主要是看到很多小伙伴使用python實(shí)現(xiàn)的,心血來潮就用go寫了下,推薦給大家。
    2015-03-03
  • Go語言使用HTTP包創(chuàng)建WEB服務(wù)器的方法

    Go語言使用HTTP包創(chuàng)建WEB服務(wù)器的方法

    這篇文章主要介紹了Go語言使用HTTP包創(chuàng)建WEB服務(wù)器的方法,結(jié)合實(shí)例形式分析了Go語言基于HTTP包創(chuàng)建WEB服務(wù)器客戶端與服務(wù)器端的實(shí)現(xiàn)方法與相關(guān)注意事項(xiàng),需要的朋友可以參考下
    2016-07-07
  • Go并發(fā)4種方法簡明講解

    Go并發(fā)4種方法簡明講解

    這篇文章主要介紹了Go并發(fā)4種方法簡明講解,需要的朋友可以參考下
    2022-04-04
  • Go語言如何實(shí)現(xiàn)TCP通信詳解

    Go語言如何實(shí)現(xiàn)TCP通信詳解

    go里面實(shí)現(xiàn)tcp沒有像之前寫的C++那些那么麻煩,在C++里面要先創(chuàng)建套接字,然后綁定ip地址,go里面直接就一個(gè)函數(shù)建立套接字,然后在進(jìn)行通信就可以了,下面這篇文章主要給大家介紹了關(guān)于Go語言如何實(shí)現(xiàn)TCP通信的相關(guān)資料,需要的朋友可以參考下
    2023-01-01

最新評論