在Golang中,空接口 interface{}
沒有定義任何函數,所以Golang 中全部類型都實現了空接口。當一個函數的形參是interface{}
,那麼在函數中,須要對形參進行斷言,從而獲得它的真實類型。函數
在學習接口斷言以前,先了解一下類型斷言,其實接口斷言也是在判斷類型。學習
類型斷言,經過它能夠作到如下幾件事情:測試
nil
某個類型
一般有兩種方式:
第一種:url
t := i.(T)
複製代碼
這個表達式能夠斷言一個接口對象i
裏不是 nil
,而且接口對象i
存儲的值的類型是 T
,若是斷言成功,就會返回值給t
,若是斷言失敗,就會觸發 panic
。spa
t := i.(T)
經常使用於 switch
結構。指針
第二種:code
t, ok:= i.(T)
複製代碼
這個表達式也是能夠斷言一個接口對象t
裏不是 nil
,而且接口對象t
存儲的值的類型是 T
;對象
若是斷言成功,就會返回其類型給t
,而且此時 ok
的值 爲 true
,表示斷言成功。
若是接口值的類型,並非咱們所斷言的 T
,就會斷言失敗,但和第一種表達式不一樣的事,這個不會觸發 panic
,而是將 ok 的值設爲 false
,表示斷言失敗,此時t
爲 T
的零值。
t, ok:= i.(T)
經常使用於 if else
結構。
t, ok := i.(T)
斷言在上一小節已經介紹過了,本小節,咱們經過實戰加深下理解。
咱們先建立一個Shape
形狀接口,兩個結構體。
// 定義接口
type Shape interface {
perimeter() float64 // 返回形狀的周長
area() float64 // 返回形狀的面積
}
// 定義結構體 type Circle struct { radius float64 } type Triangle struct { a, b, c float64 } 複製代碼
其中,Shape
接口有兩個方法,分別是求形狀的周長和麪積。
兩個結構體分別定義了本身獨有的屬性:
Circle
(圓),定義了半徑
Triangle
(三角形),定義了三條邊
接下來,咱們實現Shape
接口中的方法:
// 圓結構體 實現接口方法
func (c Circle) perimeter() float64 {
return c.radius * math.Pi * 2
}
func (c Circle) area() float64 { return math.Pow(c.radius, 2) * math.Pi } // 三角形結構體 實現接口方法 func (t Triangle) perimeter() float64 { return t.a + t.b + t.c } func (t Triangle) area() float64 { p := t.perimeter() / 2 return math.Sqrt(p * (p - t.a) * (p - t.b) * (p - t.c)) } 複製代碼
其中三角形的面積計算使用了 海倫公式
接下來咱們封裝一個接口斷言函數:
// 定義接口斷言函數
func getInterfaceType(s Shape) {
if ins, ok := s.(Triangle); ok {
fmt.Println("是三角形,三邊分別爲:", ins.a, ins.b, ins.c)
} else if ins, ok := s.(Circle); ok {
fmt.Println("是圓形,半徑爲;", ins.radius)
} else if ins, ok := s.(*Circle); ok {
fmt.Printf("是圓形結構體指針,類型爲:%T,存儲的地址爲:%p,指針自身的地址爲:%p\n", ins, &ins, ins)
} else {
fmt.Println("沒法判斷類型...")
}
}
複製代碼
該函數中不只判斷了值傳遞的類型,也判斷了引用傳遞(指針類型)的類型。由於Struct
是值類型,因此咱們加入引用類型,使練習更嚴謹一點。
接下來開始初始化結構體:
// 初始化一個圓結構體
c1 := Circle{radius: 10}
fmt.Println("==================圓結構體:==================")
fmt.Println("圓的周長爲:", c1.perimeter())
fmt.Println("圓的面積爲:", c1.area())
// 初始化一個三角形結構體 t1 := Triangle{ a: 3, b: 4, c: 5, } fmt.Println("================三角形結構體:=================") fmt.Println("三角形的周長爲:", t1.perimeter()) fmt.Println("三角形的面積爲:", t1.area()) // 初始化一個圓形結構體指針 var c2 *Circle = &Circle{radius: 5} fmt.Println("================圓形結構體指針:===============") fmt.Println("圓的周長爲:", c2.perimeter()) fmt.Println("圓的面積爲:", c2.area()) 複製代碼
輸出:
==================圓結構體:==================
圓的周長爲: 62.83185307179586
圓的面積爲: 314.1592653589793
================三角形結構體:=================
三角形的周長爲: 12
三角形的面積爲: 6
================圓形結構體指針:===============
圓的周長爲: 31.41592653589793
圓的面積爲: 78.53981633974483
複製代碼
能夠看到,以上結構體都實現了Shape
接口, 接下來開始進行接口斷言:
fmt.Println("==============t, ok:= i.(T) 開始接口斷言====================")
getInterfaceType(c1) // 判斷該接口是否爲 圓形結構體類型
getInterfaceType(t1) // 判斷該接口是否爲 圓形結構體類型
getInterfaceType(c2) // 判斷該接口是否爲 圓形結構體指針類型
複製代碼
輸出:
==============t, ok:= i.(T) 開始接口斷言===================
是圓形,半徑爲; 10
是三角形,三邊分別爲: 3 4 5
是圓形結構體指針,類型爲:*main.Circle,存儲的地址爲:0xc000006030,指針自身的地
址爲:0xc0000140e0
複製代碼
能夠看到,咱們的接口斷言奏效了,而且輸出了對應邏輯的結果。
斷言其實還有另外一種形式,就是用在利用switch
語句判斷接口的類型。
每個case
會被順序地考慮。當命中一個case
時,就會執行 case
中的語句。
所以 case
語句的順序是很重要的,由於頗有可能會有多個 case
匹配的狀況。
咱們再封裝一個 switch
邏輯的接口斷言函數,邏輯和以前的如出一轍,只是條件語句換成了 switch....case
:
// 定義接口斷言函數,使用 switch
func getInterfaceTypeSwitch(s Shape) {
switch ins := s.(type) { // 首字母小寫的 type
case Circle:
fmt.Println("是圓形,半徑爲;", ins.radius)
case Triangle:
fmt.Println("是三角形,三邊分別爲:", ins.a, ins.b, ins.c)
case *Circle:
fmt.Printf("是圓形結構體指針,類型爲:%T,存儲的地址爲:%p,指針自身的地址爲:%p\n", ins, &ins, ins)
default:
fmt.Println("沒法判斷類型...")
}
}
複製代碼
接下來測試封裝的函數:
fmt.Println("==============t := i.(type) 開始接口斷言====================")
getInterfaceTypeSwitch(c1) // 判斷該接口是否爲 圓形結構體類型
getInterfaceTypeSwitch(t1) // 判斷該接口是否爲 圓形結構體類型
getInterfaceTypeSwitch(c2) // 判斷該接口是否爲 圓形結構體指針類型
複製代碼
輸出:
==============t := i.(type) 開始接口斷言====================
是圓形,半徑爲; 10
是三角形,三邊分別爲: 3 4 5
是圓形結構體指針,類型爲:*main.Circle,存儲的地址爲:0xc000006038,指針自身的地
址爲:0xc0000140e0
複製代碼
能夠看到,switch
斷言的邏輯也正常輸出了。
總結一下,今天主要記錄了接口如何斷言的,一般有兩種方式:
t, ok:= i.(T)
t
,而且此時
ok
的值 爲
true
,表示斷言成功
ok
爲
false
,
t
爲
T
的零值
if else
結構
t := i.(T)
i
裏不是
nil
,而且接口對象
i
存儲的值的類型是
T
t
,若是斷言失敗,就會觸發
panic
switch
結構
本文使用 mdnice 排版