一、interface簡介
interface(接口)是golang最重要的特性之一,Interface類型可以定義一組方法,但是這些不需要實現。並且interface不能包含任何變量。
簡單的說:
- interface是方法的集合
- interface是一種類型,並且是指針類型
- interface的更重要的作用在於多態實現
interface定義
type 接口名稱 interface { method1 (參數列表) 返回值列表 method2 (參數列表) 返回值列表 ... }
interface使用
- 接口的使用不僅僅針對結構體,自定義類型、變量等等都可以實現接口。
- 如果一個接口沒有任何方法,我們稱為空接口,由於空接口沒有方法,所以任何類型都實現了空接口。
- 要實現一個接口,必須實現該接口里面的所有方法。
package main import "fmt" //定義接口 type Skills interface { Running() Getname() string } type Student struct { Name string Age int } // 實現接口 func (p Student) Getname() string{ //實現Getname方法 fmt.Println(p.Name ) return p.Name } func (p Student) Running() { // 實現 Running方法 fmt.Printf("%s running",p.Name) } func main() { var skill Skills var stu1 Student stu1.Name = "wd" stu1.Age = 22 skill = stu1 skill.Running() //調用接口 } //wd running
多態
上面提到了,go語言中interface是實現多態的一種形式,所謂多態,就是一種事物的多種形態,與python中類的多態是一致的。
同一個interface,不同的類型實現,都可以進行調用,它們都按照統一接口進行操作。
在上面的示例中,我們增加一個Teacher結構體,同樣實現接口進行說明:
package main import "fmt" type Skills interface { Running() Getname() string } type Student struct { Name string Age int } type Teacher struct { Name string Salary int } func (p Student) Getname() string{ //實現Getname方法 fmt.Println(p.Name ) return p.Name } func (p Student) Running() { // 實現 Running方法 fmt.Printf("%s running",p.Name) } func (p Teacher) Getname() string{ //實現Getname方法 fmt.Println(p.Name ) return p.Name } func (p Teacher) Running() { // 實現 Running方法 fmt.Printf("\n%s running",p.Name) } func main() { var skill Skills var stu1 Student var t1 Teacher t1.Name = "wang" stu1.Name = "wd" stu1.Age = 22 skill = stu1 skill.Running() skill = t1 t1.Running() } //wd running //wang running
接口嵌套
go語言中的接口可以嵌套,可以理解我繼承,子接口擁有父接口的所有方法,並且想要使用該子接口的話,必須將父接口和子接口的所有方法都實現。
type Skills interface { Running() Getname() string } type Test interface { sleeping() Skills //繼承Skills }
類型轉換
由於接口是一般類型,當我們使用接口時候可能不知道它是那個類型實現的,基本數據類型我們有對應的方法進行類型轉換,當然接口類型也有類型轉換。
當然我們也可以用這個方式來進行類型的判斷。
轉換方式:
var s int var x interface x = s y , ok := x.(int) //將interface 轉為int,ok可省略 但是省略以后轉換失敗會報錯,true轉換成功,false轉換失敗, 並采用默認值
示例:
package main import "fmt" func main() { var x interface{} s := "WD" x = s y,ok := x.(int) z,ok1 := x.(string) fmt.Println(y,ok) fmt.Println(z,ok1) } //0 false //WD true
判斷類型示例:
package main import "fmt" type Student struct { Name string } func TestType(items ...interface{}) { for k, v := range items { switch v.(type) { case string: fmt.Printf("type is string, %d[%v]\n", k, v) case bool: fmt.Printf("type is bool, %d[%v]\n", k, v) case int: fmt.Printf("type is int, %d[%v]\n", k, v) case float32, float64: fmt.Printf("type is float, %d[%v]\n", k, v) case Student: fmt.Printf("type is Student, %d[%v]\n", k, v) case *Student: fmt.Printf("type is Student, %d[%p]\n", k, v) } } } func main() { var stu Student TestType("WD", 100, stu,3.3) } //type is string, 0[WD] //type is int, 1[100] //type is Student, 2[{}] //type is float, 3[3.3]
二、反射reflect
反射是程序執行時檢查其所擁有的結構。尤其是類型的一種能力。這是元編程的一種形式。它同一時候也是造成混淆的重要來源。
每一個語言的反射模型都不同(同一時候很多語言根本不支持反射,python通過hasattr方法實現)
go語言中的反射通過refect包實現,reflect包實現了運行時反射,允許程序操作任意類型的對象。
在介紹反射之前先說明下reflect包中的兩個數據類Type和Value。
Type
Type:Type類型用來表示一個go類型。
不是所有go類型的Type值都能使用所有方法。請參見每個方法的文檔獲取使用限制。在調用有分類限定的方法時,應先使用Kind方法獲知類型的分類。調用該分類不支持的方法會導致運行時的panic。
獲取Type對象的方法:
func TypeOf(i interface{}) Type
示例:
package main import ( "reflect" "fmt" ) func main() { str := "wd" res_type := reflect.TypeOf(str) fmt.Println(res_type) //string }
reflect.Type中方法
通用方法:
// 通用方法 func (t *rtype) String() string // 獲取 t 類型的字符串描述,不要通過 String 來判斷兩種類型是否一致。 func (t *rtype) Name() string // 獲取 t 類型在其包中定義的名稱,未命名類型則返回空字符串。 func (t *rtype) PkgPath() string // 獲取 t 類型所在包的名稱,未命名類型則返回空字符串。 func (t *rtype) Kind() reflect.Kind // 獲取 t 類型的類別。 func (t *rtype) Size() uintptr // 獲取 t 類型的值在分配內存時的大小,功能和 unsafe.SizeOf 一樣。 func (t *rtype) Align() int // 獲取 t 類型的值在分配內存時的字節對齊值。 func (t *rtype) FieldAlign() int // 獲取 t 類型的值作為結構體字段時的字節對齊值。 func (t *rtype) NumMethod() int // 獲取 t 類型的方法數量。 func (t *rtype) Method() reflect.Method // 根據索引獲取 t 類型的方法,如果方法不存在,則 panic。 // 如果 t 是一個實際的類型,則返回值的 Type 和 Func 字段會列出接收者。 // 如果 t 只是一個接口,則返回值的 Type 不列出接收者,Func 為空值。 func (t *rtype) MethodByName(string) (reflect.Method, bool) // 根據名稱獲取 t 類型的方法。 func (t *rtype) Implements(u reflect.Type) bool // 判斷 t 類型是否實現了 u 接口。 func (t *rtype) ConvertibleTo(u reflect.Type) bool // 判斷 t 類型的值可否轉換為 u 類型。 func (t *rtype) AssignableTo(u reflect.Type) bool // 判斷 t 類型的值可否賦值給 u 類型。 func (t *rtype) Comparable() bool // 判斷 t 類型的值可否進行比較操作
####注意對於:數組、切片、映射、通道、指針、接口 func (t *rtype) Elem() reflect.Type // 獲取元素類型、獲取指針所指對象類型,獲取接口的動態類型
示例:
package main import ( "fmt" "reflect" ) type Skills interface { reading() running() } type Student struct { Name string Age int } func (self Student) runing(){ fmt.Printf("%s is running\n",self.Name) } func (self Student) reading(){ fmt.Printf("%s is reading\n" ,self.Name) } func main() { stu1 := Student{Name:"wd",Age:22} inf := new(Skills) stu_type := reflect.TypeOf(stu1) inf_type := reflect.TypeOf(inf).Elem() // 特別說明,引用類型需要用Elem()獲取指針所指的對象類型 fmt.Println(stu_type.String()) //main.Student fmt.Println(stu_type.Name()) //Student fmt.Println(stu_type.PkgPath()) //main fmt.Println(stu_type.Kind()) //struct fmt.Println(stu_type.Size()) //24 fmt.Println(inf_type.NumMethod()) //2 fmt.Println(inf_type.Method(0),inf_type.Method(0).Name) // {reading main func() <invalid Value> 0} reading fmt.Println(inf_type.MethodByName("reading")) //{reading main func() <invalid Value> 0} true }
其他方法:
// 數值 func (t *rtype) Bits() int // 獲取數值類型的位寬,t 必須是整型、浮點型、復數型 ------------------------------ // 數組 func (t *rtype) Len() int // 獲取數組的元素個數 ------------------------------ // 映射 func (t *rtype) Key() reflect.Type // 獲取映射的鍵類型 ------------------------------ // 通道 func (t *rtype) ChanDir() reflect.ChanDir // 獲取通道的方向 ------------------------------ // 結構體 func (t *rtype) NumField() int // 獲取字段數量 func (t *rtype) Field(int) reflect.StructField // 根據索引獲取字段 func (t *rtype) FieldByName(string) (reflect.StructField, bool) // 根據名稱獲取字段 func (t *rtype) FieldByNameFunc(match func(string) bool) (reflect.StructField, bool) // 根據指定的匹配函數 math 獲取字段 func (t *rtype) FieldByIndex(index []int) reflect.StructField // 根據索引鏈獲取嵌套字段 ------------------------------ // 函數 func (t *rtype) NumIn() int // 獲取函數的參數數量 func (t *rtype) In(int) reflect.Type // 根據索引獲取函數的參數信息 func (t *rtype) NumOut() int // 獲取函數的返回值數量 func (t *rtype) Out(int) reflect.Type // 根據索引獲取函數的返回值信息 func (t *rtype) IsVariadic() bool // 判斷函數是否具有可變參數。 // 如果有可變參數,則 t.In(t.NumIn()-1) 將返回一個切片。
示例:
package main import ( "fmt" "reflect" ) type Skills interface { reading() running() } type Student struct { Name string Age int } func (self Student) runing(){ fmt.Printf("%s is running\n",self.Name) } func (self Student) reading(){ fmt.Printf("%s is reading\n" ,self.Name) } func main() { stu1 := Student{Name:"wd",Age:22} stu_type := reflect.TypeOf(stu1) fmt.Println(stu_type.NumField()) //2 fmt.Println(stu_type.Field(0)) //{Name string 0 [0] false} fmt.Println(stu_type.FieldByName("Age")) //{{Age int 16 [1] false} true }
Value
不是所有go類型值的Value表示都能使用所有方法。請參見每個方法的文檔獲取使用限制。在調用有分類限定的方法時,應先使用Kind方法獲知該值的分類。調用該分類不支持的方法會導致運行時的panic。
Value為go值提供了反射接口,獲取Value對象方法:
func ValueOf(i interface{}) Value
示例:
str := "wd" val := reflect.ValueOf(str) //wd
reflect.Value方法
注意:以下所有方法中的v是reflect.Value返回的值。
reflect.Value.Kind():獲取變量類別,返回常量

const ( Invalid Kind = iota Bool Int Int8 Int16 Int32 Int64 Uint Uint8 Uint16 Uint32 Uint64 Uintptr Float32 Float64 Complex64 Complex128 Array Chan Func Interface Map Ptr Slice String Struct UnsafePointer )
package main import ( "reflect" "fmt" ) func main() { str := "wd" val := reflect.ValueOf(str).Kind() fmt.Println(val)//string }
用於獲取值方法:
func (v Value) Int() int64 // 獲取int類型值,如果 v 值不是有符號整型,則 panic。 func (v Value) Uint() uint64 // 獲取unit類型的值,如果 v 值不是無符號整型(包括 uintptr),則 panic。 func (v Value) Float() float64 // 獲取float類型的值,如果 v 值不是浮點型,則 panic。 func (v Value) Complex() complex128 // 獲取復數類型的值,如果 v 值不是復數型,則 panic。 func (v Value) Bool() bool // 獲取布爾類型的值,如果 v 值不是布爾型,則 panic。 func (v Value) Len() int // 獲取 v 值的長度,v 值必須是字符串、數組、切片、映射、通道。 func (v Value) Cap() int // 獲取 v 值的容量,v 值必須是數值、切片、通道。 func (v Value) Index(i int) reflect.Value // 獲取 v 值的第 i 個元素,v 值必須是字符串、數組、切片,i 不能超出范圍。 func (v Value) Bytes() []byte // 獲取字節類型的值,如果 v 值不是字節切片,則 panic。 func (v Value) Slice(i, j int) reflect.Value // 獲取 v 值的切片,切片長度 = j - i,切片容量 = v.Cap() - i。 // v 必須是字符串、數值、切片,如果是數組則必須可尋址。i 不能超出范圍。 func (v Value) Slice3(i, j, k int) reflect.Value // 獲取 v 值的切片,切片長度 = j - i,切片容量 = k - i。 // i、j、k 不能超出 v 的容量。i <= j <= k。 // v 必須是字符串、數值、切片,如果是數組則必須可尋址。i 不能超出范圍。 func (v Value) MapIndex(key Value) reflect.Value // 根據 key 鍵獲取 v 值的內容,v 值必須是映射。 // 如果指定的元素不存在,或 v 值是未初始化的映射,則返回零值(reflect.ValueOf(nil)) func (v Value) MapKeys() []reflect.Value // 獲取 v 值的所有鍵的無序列表,v 值必須是映射。 // 如果 v 值是未初始化的映射,則返回空列表。 func (v Value) OverflowInt(x int64) bool // 判斷 x 是否超出 v 值的取值范圍,v 值必須是有符號整型。 func (v Value) OverflowUint(x uint64) bool // 判斷 x 是否超出 v 值的取值范圍,v 值必須是無符號整型。 func (v Value) OverflowFloat(x float64) bool // 判斷 x 是否超出 v 值的取值范圍,v 值必須是浮點型。 func (v Value) OverflowComplex(x complex128) bool // 判斷 x 是否超出 v 值的取值范圍,v 值必須是復數型。
設置值方法:
func (v Value) SetInt(x int64) //設置int類型的值 func (v Value) SetUint(x uint64) // 設置無符號整型的值 func (v Value) SetFloat(x float64) // 設置浮點類型的值 func (v Value) SetComplex(x complex128) //設置復數類型的值 func (v Value) SetBool(x bool) //設置布爾類型的值 func (v Value) SetString(x string) //設置字符串類型的值 func (v Value) SetLen(n int) // 設置切片的長度,n 不能超出范圍,不能為負數。 func (v Value) SetCap(n int) //設置切片的容量 func (v Value) SetBytes(x []byte) //設置字節類型的值 func (v Value) SetMapIndex(key, val reflect.Value) //設置map的key和value,前提必須是初始化以后,存在覆蓋、不存在添加
其他方法:
##########結構體相關: func (v Value) NumField() int // 獲取結構體字段(成員)數量 func (v Value) Field(i int) reflect.Value //根據索引獲取結構體字段 func (v Value) FieldByIndex(index []int) reflect.Value // 根據索引鏈獲取結構體嵌套字段 func (v Value) FieldByName(string) reflect.Value // 根據名稱獲取結構體的字段,不存在返回reflect.ValueOf(nil) func (v Value) FieldByNameFunc(match func(string) bool) Value // 根據匹配函數 match 獲取字段,如果沒有匹配的字段,則返回零值(reflect.ValueOf(nil)) ########通道相關: func (v Value) Send(x reflect.Value)// 發送數據(會阻塞),v 值必須是可寫通道。 func (v Value) Recv() (x reflect.Value, ok bool) // 接收數據(會阻塞),v 值必須是可讀通道。 func (v Value) TrySend(x reflect.Value) bool // 嘗試發送數據(不會阻塞),v 值必須是可寫通道。 func (v Value) TryRecv() (x reflect.Value, ok bool) // 嘗試接收數據(不會阻塞),v 值必須是可讀通道。 func (v Value) Close() // 關閉通道 ########函數相關 func (v Value) Call(in []Value) (r []Value) // 通過參數列表 in 調用 v 值所代表的函數(或方法)。函數的返回值存入 r 中返回。 // 要傳入多少參數就在 in 中存入多少元素。 // Call 即可以調用定參函數(參數數量固定),也可以調用變參函數(參數數量可變)。 func (v Value) CallSlice(in []Value) []Value // 調用變參函數
示例一:獲取和設置普通類型的值
package main import ( "reflect" "fmt" ) func main() { str := "wd" age := 11 fmt.Println(reflect.ValueOf(str).String()) //獲取str的值,結果wd fmt.Println(reflect.ValueOf(age).Int()) //獲取age的值,結果age str2 := reflect.ValueOf(&str) //獲取Value類型 str2.Elem().SetString("jack") //設置值 fmt.Println(str2.Elem(),age) //jack 11 }
示例二:簡單結構體操作
package main import ( "fmt" "reflect" ) type Skills interface { reading() running() } type Student struct { Name string Age int } func (self Student) runing(){ fmt.Printf("%s is running\n",self.Name) } func (self Student) reading(){ fmt.Printf("%s is reading\n" ,self.Name) } func main() { stu1 := Student{Name:"wd",Age:22} stu_val := reflect.ValueOf(stu1) //獲取Value類型 fmt.Println(stu_val.NumField()) //2 fmt.Println(stu_val.Field(0),stu_val.Field(1)) //wd 22 fmt.Println(stu_val.FieldByName("Age")) //22 stu_val2 := reflect.ValueOf(&stu1).Elem() stu_val2.FieldByName("Age").SetInt(33) //設置字段值 ,結果33 fmt.Println(stu1.Age) }
示例三:通過反射調用結構體中的方法,通過reflect.Value.Method(i int).Call()或者reflect.Value.MethodByName(name string).Call()實現
package main import ( "fmt" "reflect" ) type Student struct { Name string Age int } func (this *Student) SetName(name string) { this.Name = name fmt.Printf("set name %s\n",this.Name ) } func (this *Student) SetAge(age int) { this.Age = age fmt.Printf("set age %d\n",age ) } func (this *Student) String() string { fmt.Printf("this is %s\n",this.Name) return this.Name } func main() { stu1 := &Student{Name:"wd",Age:22} val := reflect.ValueOf(stu1) //獲取Value類型,也可以使用reflect.ValueOf(&stu1).Elem() val.MethodByName("String").Call(nil) //調用String方法 params := make([]reflect.Value, 1) params[0] = reflect.ValueOf(18) val.MethodByName("SetAge").Call(params) //通過名稱調用方法 params[0] = reflect.ValueOf("jack") val.Method(1).Call(params) //通過方法索引調用 fmt.Println(stu1.Name,stu1.Age) } //this is wd //set age 18 //set name jack //jack 18