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