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、调用对象接口的时候,必须取对象指针!
基本概念参考: