go語言之行--接口(interface)、反射(reflect)詳解


一、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

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM