中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Go接口怎么用

發布時間:2022-02-19 10:36:44 來源:億速云 閱讀:157 作者:小新 欄目:開發技術

這篇文章給大家分享的是有關Go接口怎么用的內容。小編覺得挺實用的,因此分享給大家做個參考,一起跟隨小編過來看看吧。

接口是一種抽象類型,是對其他類型行為的概括與抽象,從語法角度來看,接口是一組方法定義的集合。很多面向對象的語言都有接口這個概念,但Go語言接口的獨特之處在于它是隱式實現。

Go接口怎么用

1.接口

在Go中使用interface關鍵字聲明一個接口:

type Shaper interface {
Area() float64
Perimeter() float64
}

如果我們直接使用fmt庫進行輸出,會得到什么結果呢?

func main() {
var s Shaper
fmt.Println("value of s is ", s)
fmt.Printf("type of s is %T\n", s)
}

輸出:

value of s is  type of s is

在這里,引出接口的概念。接口有兩種類型。接口的靜態類型是接口本身,例如上述程序中的Shape。接口沒有靜態值,而是指向動態值。

接口類型的變量可以保存實現接口的類型的值。該類型的值成為接口的動態值,并且該類型成為接口的動態類型。

從上面的示例開始,我們可以看到零值和接口的類型為nil。這是因為,此刻,我們已聲明類型Shaper的變量s,但未分配任何值。當我們使用帶有接口參數的fmt包中的Println函數時,它指向接口的動態值,Printf功能中的%T語法是指動態類型的接口。實際上,接口靜態類型是Shaper。

當我們使用一個類型去實現該接口后,會是什么效果。

type Rect struct {
width  float64
height float64
}

func (r Rect) Area() float64 {return r.width * r.height
}

func (r Rect) Perimeter() float64 {return 2 * (r.width + r.height)
}

// main
func main() {
var s Shaper
fmt.Println("value of s is ", s)
fmt.Printf("type of s is %T\n", s)
s = Rect{5.0, 4.0}
r := Rect{5.0, 4.0}
fmt.Printf("type of s is %T\n", s)
fmt.Printf("value of s is %v\n", s)
fmt.Printf("area of rect is %v\n", s.Area())
fmt.Println("s == r is", s == r)
}

輸出:

value of s is  type of s is  type of s is main.Rect
value of s is {5 4}
area of rect is 20
s == r is tru

可以看到此時s變成了動態類型,存儲的是main.Rect,值變成了{5,4}。

有時,動態類型的接口也稱為具體類型,因為當我們訪問接口類型時,它會返回其底層動態值的類型,并且其靜態類型保持隱藏。

我們可以在s上調用Area方法,因為接口Shaper定義了Area方法,而s的具體類型是Rect,它實現了Area方法。該方法將在接口保存的動態值上被調用。

此外,我們可以看到我們可以使用s與r進行比較,因為這兩個變量都保存相同的動態類型(Rect類型的結構)和動態值{5 4}。

我們接著使用圓來實現該接口:

type Circle struct {
radius float64
}

func (c Circle) Area() float64 {return 3.14 * c.radius * c.radius
}

func (c Circle) Perimeter() float64 {return 2 * 3.14 * c.radius
}
// main
s = Circle{10}
fmt.Printf("type of s is %T\n", s)
fmt.Printf("value of s is %v\n", s)
fmt.Printf("area of rect is %v\n", s.Area())

此時輸出:

type of s is main.Circle
value of s is {10}
area of rect is 314

這里進一步理解了接口保存的動態類型。從切片角度出發,可以說,接口也以類似的方式工作,即動態保存對底層類型的引用。

當我們刪除掉Perimeter的實現,可以看到如下報錯結果。

./rect.go:34:4: cannot use Rect{...} (type Rect) as type Shaper in assignment:
Rect does not implement Shaper (missing Perimeter method)

從上面的錯誤應該是顯而易見的,為了成功實現接口,需要實現與完全簽名的接口聲明的所有方法。

2.空接口

當一個接口沒有任何方法時,它被稱為空接口。這由接口{}表示。因為空接口沒有方法,所以所有類型都隱式地實現了這個接口。

空接口的作用之一在于:函數可以接收多個不同類型參數。

例如:fmt的Println函數。

func Println(a ...interface{}) (n int, err error)Println是一個可變函數,它接受interface{}類型的參數。

例如:

type MyString stringfunc explain(i interface{}) {fmt.Printf("type: %T, value: %v\n", i, i)}// mains := MyString("hello")explain(s)r := Rect{1, 2}explain(r)

輸出:

type: inter.MyString, value: hellotype: inter.Rect, value: {1 2}

可以看到空接口的類型與值是動態的。

3.多個接口

在下面的程序中,我們用Area方法創建了Shape接口,用Volume方法創建了Object接口。因為結構類型Cube實現了這兩個方法,所以它實現了這兩個接口。因此,我們可以將結構類型Cube的值賦給類型為Shape或Object的變量。

type IShape interface {Area() float64}type Object interface {Volume() float64}type Cube struct {side float64}func (c Cube) Area() float64 {return 6 * c.side * c.side}func (c Cube) Volume() float64 {return c.side * c.side * c.side}// mainc := Cube{3}var s IShape = cvar o Object = cfmt.Println("area is", s.Area())fmt.Println("Volume is", o.Volume())

這種調用是沒有問題的,調用各自動態類型的方法。

那如果是這樣呢?

fmt.Println("area of s of interface type IShape is", s.Volume())fmt.Println("volume of o of interface type Object is", o.Area())

輸出:

s.Volume undefined (type Shape has no field or method Volume)o.Area undefined (type Object has no field or method Area)

這個程序無法編譯,因為s的靜態類型是IShape,而o的靜態類型是Object。因為IShape沒有定義Volume方法,Object也沒有定義Area方法,所以我們得到了上面的錯誤。

要使其工作,我們需要以某種方式提取這些接口的動態值,這是一個立方體類型的結構體,立方體實現了這些方法。這可以使用類型斷言來完成。

4.類型斷言

我們可以通過i.(Type)確定接口i的底層動態值,Go將檢查i的動態類型是否與type相同,并返回可能的動態值。

var s1 IShape = Cube{3}c1 := s1.(Cube)fmt.Println("area of s of interface type IShape is", c1.Volume())fmt.Println("volume of o of interface type Object is", c1.Area())

這樣便可以正常工作了。

如果IShape沒有存儲Cube類型,且Cube沒有實現IShape,那么報錯:

impossible type assertion:Cube does not implement IShape (missing Area method)

如果IShape沒有存儲Cube類型,且Cube實現Shape,那么報錯:

panic: interface conversion: inter.IShape is nil, not inter.Cub

幸運的是,語法中還有另一個返回值:

value, ok := i.(Type)

在上面的語法中,如果i有具體的type類型或type的動態值,我們可以使用ok變量來檢查。如果不是,那么ok將為假,value將為Type的零值(nil)。

此外,使用類型斷言可以檢查該接口的動態類型是否實現了其他接口,就像前面的IShape的動態類型是Cube,它實現了IShape、Object接口,如下例子:

vaule1, ok1 := s1.(Object)value2, ok2 := s1.(Skin)fmt.Printf("IShape s的動態類型值是: %v, 該動態類型是否實現了Object接口: %v\n", vaule1, ok1)fmt.Printf("IShape s的動態類型值是: %v, 該動態類型是否實現了Skin接口: %v\n", value2, ok2)

輸出:

IShape s的動態類型值是: {3}, 該動態類型是否實現了Object接口: trueIShape s的動態類型值是: , 該動態類型是否實現了Skin接口: false

類型斷言不僅用于檢查接口是否具有某個給定類型的具體值,而且還用于將接口類型的給定變量轉換為不同的接口類型。

5.類型Switch

在前面的空接口中,我們知道將一個空接口作為函數參數,那么該函數可以接受任意類型,那如果我有一個需求是:當傳遞的數據類型是字符串時,要求全部變為大寫,其他類型不進行操作?

針對這樣的需求,我們可以采用Type Switch,即:i.(type)+switch。

func switchProcess(i interface{}) {switch i.(type) {case string: fmt.Println("process string")case int: fmt.Println("process int")default: fmt.Printf("type is %T\n", i)}}

輸出:

process intprocess string

6.嵌入接口

在Go中,一個接口不能實現或擴展其他接口,但我們可以通過合并兩個或多個接口來創建一個新的接口。

例如:

這里使用Runner與Eater兩個接口,組合成了一個新接口RunEater,該接口為Embedding interfaces。

type Runner interface {
run() string
}type Eater interface {
eat() string
}type RunEater interface {
Runner
Eater
}type Dog struct {
age int
}

func (d Dog) run() string {return "run"}

func (d Dog) eat() string {return "eat"}

// main
d := Dog{10}
var re RunEater = d
var r Runner = d
var e Eater = d
fmt.Printf("RunnEater dynamic type: %T, value: %v\n", re, re)
fmt.Printf("Runn dynamic type: %T, value: %v\n", r, r)
fmt.Printf("Eater dynamic type: %T, value: %v\n", e, e)

輸出:

RunnEater dynamic type: inter.Dog, value: {10}
Runn dynamic type: inter.Dog, value: {10}
Eater dynamic type: inter.Dog, value: {10}

7.接口比較

如果基礎動態值為nil,則兩個接口總是相等的,這意味著兩個nil接口總是相等的,因此== operation返回true。

var a, b interface{}
fmt.Println( a == b ) // true

如果這些接口不是nil,那么它們的動態類型(具體值的類型)應該相同,具體值應該相等。

如果接口的動態類型不具有可比性,例如slice、map、function,或者接口的具體值是包含這些不可比較性值的復雜數據結構,如切片或數組,則==或!=操作將導致運行時panic。

感謝各位的閱讀!關于“Go接口怎么用”這篇文章就分享到這里了,希望以上內容可以對大家有一定的幫助,讓大家可以學到更多知識,如果覺得文章不錯,可以把它分享出去讓更多的人看到吧!

向AI問一下細節
推薦閱讀:
  1. Go36-14-接口
  2. Go的空接口

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

go
AI

河间市| 和硕县| 益阳市| 长子县| 滨海县| 灯塔市| 石渠县| 德兴市| 通渭县| 淅川县| 临漳县| 灵宝市| 石河子市| 宣恩县| 平山县| 永安市| 清镇市| 敖汉旗| 文登市| 竹山县| 会理县| 淮阳县| 正宁县| 云龙县| 大理市| 陆河县| 衡阳市| 东乌珠穆沁旗| 肥西县| 三明市| 尚义县| 姚安县| 舞阳县| 新营市| 仁怀市| 铜山县| 什邡市| 池州市| 昂仁县| 阜新市| 涟源市|