Golang介面斷言學習
在Golang中,空介面 interface{}
沒有定義任何函式,因此Golang 中所有型別都實現了空介面。當一個函式的形參是interface{}
,那麼在函式中,需要對形參進行斷言,從而得到它的真實型別。
在學習介面斷言之前,先了解一下型別斷言,其實介面斷言也是在判斷型別。
型別斷言,通過它可以做到以下幾件事情:
- 檢查 i 是否為
nil
- 檢查 i 儲存的值是否為
某個型別
通常有兩種方式:
第一種:
t := i.(T)
這個表示式可以斷言一個介面物件i
裡不是 nil
,並且介面物件i
儲存的值的型別是 T
,如果斷言成功,就會返回值給t
,如果斷言失敗,就會觸發 panic
。
t := i.(T)
常用於 switch
結構。
第二種:
t, ok:= i.(T)
這個表示式也是可以斷言一個介面物件t
裡不是 nil
,並且介面物件t
儲存的值的型別是 T
;
如果斷言成功,就會返回其型別給t
,並且此時 ok
的值 為 true
,表示斷言成功。
如果介面值的型別,並不是我們所斷言的 T
,就會斷言失敗,但和第一種表示式不同的事,這個不會觸發 panic
,而是將 ok 的值設為 false
,表示斷言失敗,此時t
為 T
的零值。
t, ok:= i.(T)
常用於 if else
結構。
1.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
可以看到,我們的介面斷言奏效了,並且輸出了對應邏輯的結果。
2.switch結構 介面斷言
斷言其實還有另一種形式,就是用在利用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
結構
本作品採用《CC 協議》,轉載必須註明作者和本文連結