通过反射深入学习go接口

1. 常见的interface用法

1.1 作为方法的集合,提供方法

type Animal interface {
   Eat()
}

type Dog struct {}
func (d *Dog) Eat() {
   fmt.Println("Dog eat")
}

var ani Animal
ani = &Dog{}
ani.Eat() //output: "Dog eat"

1.2 作为类型容器,接受和转换各种基础类型

var a interface{}
num:=1
a= &num

2. interface的底层结构

上面两种用法的区别就是接口里有没有实现方法。根据这个区别两者在存储时也会对应到不同的结构体。

// emptyInterface is the header for an interface{} value.
type emptyInterface struct {
    typ  *rtype
    word unsafe.Pointer
}

// nonEmptyInterface is the header for an interface value with methods.
type nonEmptyInterface struct {
    // see ../runtime/iface.go:/Itab
    itab *struct {
        ityp *rtype // static interface type
        typ  *rtype // dynamic concrete type
        hash uint32 // copy of typ.hash
        _    [4]byte
        fun  [100000]unsafe.Pointer // method table
    }
    word unsafe.Pointer
}

如上,其实两者都是只存储两个指针,其中第二个指针都是指向数据的值,区别在第一个指针。nonEmptyInterface指向的结构体会更丰富一些,包含方法的数组等。

3. 深入学习空接口

3.1 与nil比较

从各种基础类型转化为interface{},从C语言之类的来看,很像void*。但实际却完全不一样的,看下面的例子:

type User struct {
    A int
}
var inf interface{} 
fmt.Printf("inf: %+v\n", inf)
fmt.Printf("inf== nil is %+v\n", inf == nil)
fmt.Printf("inf.Type is %+v\n", reflect.TypeOf(inf))
fmt.Printf("inf.Value is %+v\n", reflect.ValueOf(inf))
/*得到结果如下:
inf: 
inf== nil is true
inf.Type is 
inf.Value is 
*/

//给inf赋值一个空的结构体指针
var p *User
inf = p
fmt.Printf("p: %+v\n", p)   //p: 
fmt.Printf("inf: %+v\n", inf)    //inf: 
fmt.Printf("inf== nil is %+v\n", inf == nil)  //inf== nil is false
fmt.Printf("inf.Type is %+v\n", reflect.TypeOf(inf)) //inf.Type is *main.User
fmt.Printf("inf.Value is %+v\n", reflect.ValueOf(inf)) //inf.Value is 
fmt.Println(reflect.ValueOf(inf).IsNil())   //true
fmt.Printf("inf.Value.Type is %+v\n", reflect.ValueOf(inf).Type()) //inf.Value.Type is *main.User
fmt.Printf("inf.Value.Kind is %+v\n", reflect.ValueOf(inf).Kind()) //inf.Value.Kind is ptr

这时inf是不等于nil的,从上面的空接口的结构来分析,它的Value仍然是nil,但是它的类型指针指向的结构体里类型有了新的赋值,所以看到Type是*main.User。这里比较容易埋坑!!!!!

在业务中如果有返回值是interface{},在判断是否有值时,最好用

reflect.ValueOf(inf).IsNil()

还有就是reflect.Value的Type与Kind方法区别,Type时动态类型为*main.User,而Kind只提示是何种基础类型。

inf = int(1)
fmt.Printf("inf.Value.Type is %+v\n", reflect.ValueOf(inf).Type()) //inf.Value.Type is int
fmt.Printf("inf.Value.Kind is %+v\n", reflect.ValueOf(inf).Kind())  //inf.Value.Kind is int

当为int时,二者是一致的

3.2 通过反射赋值

将数据传给interface{}后,可以通过反射来改变原来的值

n := 1
inf = &n
reflect.ValueOf(inf).Elem().SetInt(2)
fmt.Printf("%+v\n", n) //2

go 语言的函数调用都是值传递的,所以我们只能先获取指针对应的 reflect.Value,再通过 reflect.Value.Elem 方法迂回的方式得到可以被设置的变量

那能否通过断言改变值呢,答案是不行的

p := inf.(*int)
a := 2
p = &a  //p只能调用,不能传值

3.3 嵌套interface{}

测试一下: []interface{}传给interface{},观察一下,它的类型是什么

func Interface() interface{} {
    return SliceInterface()
}

func SliceInterface() []interface{} {
    var res []interface{}
    arr := []int{1, 2, 3, 4}
    for _, val := range arr {
        res = append(res, val)
    }
    return res
}

inf = Interface()
fmt.Printf("inf.Type is %+v\n", reflect.TypeOf(inf))
fmt.Printf("inf.Value is %+v\n", reflect.ValueOf(inf))
fmt.Printf("inf.Value.Kind is %+v\n", reflect.ValueOf(inf).Kind())
fmt.Printf("inf.Value[0].Type is %+v\n", reflect.ValueOf(inf).Index(0).Type())
ind0 := reflect.ValueOf(inf).Index(0).Interface()
fmt.Printf("inf.Value[0].Type is %+v\n", reflect.TypeOf(ind0))
fmt.Printf("inf.Value[0].Value is %+v\n", reflect.ValueOf(ind0))
/*输出如下:
inf.Type is []interface {}
inf.Value is [1 2 3 4]
inf.Value.Kind is slice
inf.Value[0].Type is interface {}
inf.Value[0].Type is int
inf.Value[0].Value is 1
*/

即interface{}还是作为类型存入,只会循环嵌套,只有找到最里面一层的interface{}才可以读到具体的值

4. 本文参考:

https://draveness.me/golang/docs/part2-foundation/ch04-basic/golang-interface/

猜你喜欢

转载自blog.csdn.net/yshhuan/article/details/110469077