Golang关于类型设计的一些原则
  • 变量包括(type, value)两部分
  • 理解这一点就知道为什么nil != nil了
  • type 包括 static type和concrete type. 简单来说 static type是你在编码是看见的类型(如int、string),concrete type是runtime系统看见的类型
  • 类型断言能否成功,取决于变量的concrete type,而不是static type. 因此,一个 reader变量如果它的concrete type也实现了write方法的话,它也可以被类型断言为writer.

 

  golang反射常见场景
  • 1、框架接口的入参。
  • 2、基类寻找子类的类型,并调用其变量、方法。

 

  示例代码,包含了常见reflect的使用场景: 

  通过反射获取结构体成员变量、接口

 1 type User struct {
 2     Name string
 3     Age  int
 4 }
 5 
 6 func TestReflect1(t *testing.T) {
 7     u := User{
 8         Name: "myName",
 9         Age:  23,
10     }
11     getType := reflect.TypeOf(u)                //获取类型
12     fmt.Println(getType)
13     getPointType := reflect.TypeOf(&u)            //获取指针类型
14     fmt.Println(getPointType)
15     getValue := reflect.ValueOf(u)                //获取值
16     fmt.Println(getValue)
17     for i := 0; i < getType.NumField(); i++ {    //取各成员变量
18         field := getType.Field(i)
19         value := getValue.Field(i).Interface()
20         fmt.Printf("%s: %v = %v\n", field.Name, field.Type, value)
21     }
22     for i := 0; i < getPointType.NumMethod(); i++ {    //获取方法
23         m := getPointType.Method(i)
24         fmt.Printf("%s: %v\n", m.Name, m.Type)
25     }
26     time.Sleep(time.Second)
27 }

  调试打印:

API server listening at: 127.0.0.1:61695
=== RUN   TestReflect1
reflect.User
*reflect.User
{myName 23}
Name: string = myName
Age: int = 23
--- PASS: TestReflect1 (1.00s)
PASS

Debugger finished with exit code 0

 

  通过反射对结构体成员变量进行修改

 1 func TestReflect2(t *testing.T) {
 2     type User struct {
 3         Name string
 4         Age  int
 5     }
 6     u := User{
 7         Name: "myName",
 8         Age:  23,
 9     }
10     getType := reflect.TypeOf(u)                //获取类型
11     fmt.Println(getType)
12     getPointType := reflect.TypeOf(&u)            //获取指针类型
13     fmt.Println(getPointType)
14     getValue := reflect.ValueOf(u)                //获取值
15     fmt.Println(getValue)
16     getPointValue := reflect.ValueOf(&u)        //获取值指针
17     fmt.Println(getPointValue)
18     for i := 0; i < getType.NumField(); i++ {    //取各成员变量
19         field := getType.Field(i)
20         value := getValue.Field(i).Interface()
21         fmt.Printf("%s: %v = %v\n", field.Name, field.Type, value)
22     }
23     if value := getPointValue.Elem().FieldByName("Name"); value.IsValid() {
24         value.Set(reflect.ValueOf("myNameModify"))
25     }
26     if value := getPointValue.Elem().FieldByName("Age"); value.IsValid() {
27         value.Set(reflect.ValueOf(123))
28     }
29     go func() {    //错误调用示例1,查找成员后没有检查是否合法
30         defer func() {
31             fmt.Println(recover())    //panic : reflect: call of reflect.Value.Elem on struct Value
32         }()
33         getPointValue.Elem().FieldByName("Name123").Set(reflect.ValueOf("myNameModify123"))    //Set()默认入参与变量一致,导致panic
34     }()
35     go func() {    //错误调用示例2,设置的变量类型与实际类型不符
36         defer func() {
37             fmt.Println(recover())    //panic : reflect.Set: value of type string is not assignable to type int
38         }()
39         getPointValue.Elem().FieldByName("Age").Set(reflect.ValueOf("123"))    //Set()默认入参与变量一致,导致panic
40     }()
41     go func() {    //错误调用示例3,修改成员变量需要查找指针类型
42         defer func() {
43             fmt.Println(recover())    //panic : reflect: call of reflect.flag.mustBeAssignable on zero Value
44         }()
45         getValue.Elem().FieldByName("Name").Set(reflect.ValueOf("myNameModify456"))    //Set()默认入参与变量一致,导致panic
46     }()
47     getType = reflect.TypeOf(u)                //获取类型
48     fmt.Println(u)
49     //for i := 0; i < getType.NumField(); i++ {    //取各成员变量
50     //    field := getType.Field(i)
51     //    value := getValue.Field(i).Interface()
52     //    fmt.Printf("%s: %v = %v\n", field.Name, field.Type, value)
53     //}
54     time.Sleep(time.Second)
55 }

  调试打印:

API server listening at: 127.0.0.1:61689
=== RUN   TestReflect2
reflect.User
*reflect.User
{myName 23}
&{myName 23}
Name: string = myName
Age: int = 23
{myNameModify 123}
reflect: call of reflect.Value.Elem on struct Value
reflect.Set: value of type string is not assignable to type int
reflect: call of reflect.flag.mustBeAssignable on zero Value
--- PASS: TestReflect2 (1.00s)
PASS


Debugger finished with exit code 0

  通过反射调用对象方法

 1 type User3 struct {
 2     Name string
 3     Age  int
 4 }
 5 func (i *User3) MyMethod(cb interface{}) error {
 6     fmt.Println("MyMethod is me.")
 7     f := cb.(interface{}).(func())    //确定类型直接转换
 8     f()
 9     return errors.New("MyMethod return no error!")
10 }
11 
12 func TestReflect3(t *testing.T) {
13     u := User3{}
14     f := func() {
15         fmt.Println("Func is me.")
16     }
17     go func() { //取method运行
18         getPointValue := reflect.ValueOf(&u)                //获取对象指针
19         fun := getPointValue.MethodByName("MyMethod")    //查找方法
20         input := []reflect.Value{reflect.ValueOf(f)}        //入参设定
21         values := fun.Call(input)                            //调用
22         err := values[0].Interface().(error)                //转换返回值
23         fmt.Println(err)
24         //上述一行写法
25         //fmt.Println(reflect.ValueOf(&u).MethodByName("MyMethod").Call([]reflect.Value{
26         //    reflect.ValueOf(f)})[0].Interface().(error))
27     }()
28     go func() { //错误示例:不用指针reflect,调用method、修改成员变量,均需要使用指针类型。
29         defer func() {
30             fmt.Println(recover()) //panic: reflect: call of reflect.Value.Call on zero Value
31         }()
32         getValue := reflect.ValueOf(u)                //获取值
33         values := getValue.MethodByName("MyMethod").Call([]reflect.Value{
34             reflect.ValueOf(f)})
35         err := values[0].Interface().(error)
36         fmt.Println(err)
37     }()
38     time.Sleep(time.Second)
39 }

  调试打印

API server listening at: 127.0.0.1:61729
=== RUN   TestReflect3
reflect: call of reflect.Value.Call on zero Value
MyMethod is me.
Func is me.
MyMethod return no error!
--- PASS: TestReflect3 (1.00s)
PASS


Debugger finished with exit code 0

 

  常见的坑:

  1、结构体及成员变量定义的时候,首字符大写!

    golang中首字母决定了变量的作用域。大驼峰即为全局。

  2、修改成员变量,必须取对象指针!

  3、调用对象接口的时候,必须取对象指针!

 

基本概念参考: