Golang學習——interface接口學習(二)

Golang接口斷言學習

Golang中,空接口 interface{}沒有定義任何函數,所以Golang 中全部類型都實現了空接口。當一個函數的形參是interface{},那麼在函數中,須要對形參進行斷言,從而獲得它的真實類型。函數

一.類型斷言

在學習接口斷言以前,先了解一下類型斷言,其實接口斷言也是在判斷類型。學習

類型斷言,經過它能夠作到如下幾件事情:測試

  1. 檢查 i 是否爲 nil
  2. 檢查 i 存儲的值是否爲 某個類型

一般有兩種方式:
第一種:url

t := i.(T)
複製代碼

這個表達式能夠斷言一個接口對象i裏不是 nil,而且接口對象i存儲的值的類型是 T,若是斷言成功,就會返回值給t,若是斷言失敗,就會觸發 panicspa

t := i.(T) 經常使用於 switch 結構。指針

第二種:code

t, ok:= i.(T)
複製代碼

這個表達式也是能夠斷言一個接口對象t裏不是 nil,而且接口對象t存儲的值的類型是 T;對象

若是斷言成功,就會返回其類型給t,而且此時 ok 的值 爲 true,表示斷言成功。

若是接口值的類型,並非咱們所斷言的 T,就會斷言失敗,但和第一種表達式不一樣的事,這個不會觸發 panic,而是將 ok 的值設爲 false ,表示斷言失敗,此時tT 的零值。

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斷言的邏輯也正常輸出了。

總結一下,今天主要記錄了接口如何斷言的,一般有兩種方式:

  1. 方式一: t, ok:= i.(T)
  • 斷言成功,就會返回其類型給 t,而且此時 ok 的值 爲 true,表示斷言成功
  • 斷言失敗, okfalsetT的零值
  • 一般用於 if else結構
  1. 方式二: t := i.(T)
  • 斷言一個接口對象 i裏不是 nil,而且接口對象 i存儲的值的類型是 T
  • 若是斷言成功,就會返回值給 t,若是斷言失敗,就會觸發 panic
  • 一般用於 switch結構

本文使用 mdnice 排版

相關文章
相關標籤/搜索