Go语言入门心法(十三): 反射认知升维

Go语言入门心法(十三): 反射认知升维_第1张图片


Go语言入门心法(一): 基础语法

Go语言入门心法(二): 结构体

Go语言入门心法(三): 接口

Go语言入门心法(四): 异常体系

 Go语言入门心法(五): 函数

Go语言入门心法(六): HTTP面向客户端|服务端编程

Go语言入门心法(七): 并发与通道

Go语言入门心法(八): mysql驱动安装报错onnection failed

Go语言入门心法(九): 引入三方依赖

Go语言入门心法(十):Go语言操作MYSQL(CRUD)|事务处理

Go语言入门心法(十一): 文件处理

 Go语言入门心法(十二): GORM映射框架

Go语言入门心法(十三): 反射认知升维

Go语言入门心法(十四): Go操作Redis实战 




 一: go语言反射认知

反射的强大之处就在于它非常灵活,通过用于做通用框架代码,而不需要理解业务,因此不需要具有快速处理不同业务的功能,但是强大的同时也带来了很多弊端;
比如其代码可读性和可维护性变差,性能也大打折扣;是否需要使用反射需进行利弊权衡,并不是所有的程序都适合使用反射;
   (1)go语言提供了一种反射的机制: 在运行时需要检查或者更新变量的值,并调用动态调用其方法和它们支持的内在操作,而这些操作在编译时并不知道这些变量的
   具体类型;而这种动态操作变量对象的方式就成为反射机制;
   (2)变量的类型通常分为:
      2.1 静态类型,即是在定义变量时指定的具体类型,或者在编译时就可以确定的类型
      2.2 动态类型,需要程序运行状态下才能确认的类型,如对应一个接口,有多个子接口,通常我们可以使用父接口作为数据类型定义该接口类型的变量对象,
      这时这个变量对象的值可以该接口子实现类型的接口,具体是那一个需在运行过程中动态感知;
      2.3 类别Java语言,通过反射机制在运行时能够做到下面的几点
          2.3.1 确认对象的类
          2.3.2 确认类的所有成员变量和方法
          2.3.3 动态调用任意一个方法和变量
   (3)go语言中,只能使用一种方式来获取运行状态的对象的值或者方法,最重要的是,Go语言不支持通过字符串解析,从而反射出对应的类型结构,这点与java有着很大的区别


反射实例一:


package main

import (
	"fmt"
	"reflect"
)

/*
go语言中反射认知:

		反射的强大之处就在于它非常灵活,通过用于做通用框架代码,而不需要理解业务,因此不需要具有快速处理不同业务的功能,但是强大的同时也带来了很多弊端;
		比如其代码可读性和可维护性变差,性能也大打折扣;是否需要使用反射需进行利弊权衡,并不是所有的程序都适合使用反射;
	    (1)go语言提供了一种反射的机制: 在运行时需要检查或者更新变量的值,并调用动态调用其方法和它们支持的内在操作,而这些操作在编译时并不知道这些变量的
	    具体类型;而这种动态操作变量对象的方式就成为反射机制;
	    (2)变量的类型通常分为:
	       2.1 静态类型,即是在定义变量时指定的具体类型,或者在编译时就可以确定的类型
	       2.2 动态类型,需要程序运行状态下才能确认的类型,如对应一个接口,有多个子接口,通常我们可以使用父接口作为数据类型定义该接口类型的变量对象,
	       这时这个变量对象的值可以该接口子实现类型的接口,具体是那一个需在运行过程中动态感知;
	       2.3 类别Java语言,通过反射机制在运行时能够做到下面的几点
	           2.3.1 确认对象的类
	           2.3.2 确认类的所有成员变量和方法
	           2.3.3 动态调用任意一个方法和变量
	    (3)go语言中,只能使用一种方式来获取运行状态的对象的值或者方法,最重要的是,Go语言不支持通过字符串解析,从而反射出对应的类型结构,这点与java有着很大的区别
*/
func main() {
	var result interface{} = "这是测试反射的第一个例子"
	typeOfResult := reflect.TypeOf(result)
	println("========================-获取变量result的类型===============")
	fmt.Printf("变量的静态类型为:%s", "interface{}")
	println("在变量运行状态下获取result变量的类型为:\n ", typeOfResult.Name())
	println("==========================动态获取变量result的值=============\n")
	if typeOfResult.Kind() == reflect.String {
		fmt.Printf("变量的值为: %s", typeOfResult.String())
	}

	println(`

        下面来看下kind的源代码:
        type Kind uint

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
	Pointer
	Slice
	String
	Struct
	UnsafePointer
)

    `)

}

运行效果:


GOROOT=D:\program_file_worker\go1.20 #gosetup
GOPATH=D:\program_file_worker\go1.20\bin;C:\Users\Administrator\go #gosetup
D:\program_file_worker\go1.20\bin\go.exe build -o C:\Users\Administrator\AppData\Local\Temp\GoLand\___go_build_org_jd_data_org_jd_data_reflect.exe org.jd.data/org.jd.data/reflect #gosetup
C:\Users\Administrator\AppData\Local\Temp\GoLand\___go_build_org_jd_data_org_jd_data_reflect.exe
========================-获取变量result的类型===============
变量的静态类型为:interface{}在变量运行状态下获取result变量的类型为:
  string
==========================动态获取变量result的值=============

变量的值为: string

        下面来看下kind的源代码:
        type Kind uint

const (
        Invalid Kind = iota
        Bool
        Int
        Int8
        Int16
        Int32
        Int64
        Uint
        Uint8
        Uint16
        Uint32
        Uint64
        Float32
        Float64
        Complex64
        Complex128
        Array
        Chan
        Func
        Interface
        Map
        Pointer
        Slice
        String
        Struct
        UnsafePointer
)

Process finished with the exit code 0
 

二: go语言反射中Kind类常量认知


提示: Go语言不支持解析string然后执行,Go语言的反射机制只能作用在“已经存在的对象”上

      Go语言中的反射的种类(Kind):
         type Kind uint

const (

   Invalid Kind = iota  // 非法的类型
   Bool        // 布尔型
   Int         // 有符号整形
   Int8        // 有符号8位整形
   Int16       // 有符号16位整形
   Int32       // 有符号32位整形
   Int64       // 有符号64位整形
   Uint        // 无符号整形
   Uint8       // 无符号8位整形
   Uint16      // 无符号16位整形
   Uint32      // 无符号32位整形
   Uint64      // 无符号64位整形
   Float32     // 单精度浮点数
   Float64      // 双精度浮点数
   Complex64    // 64复数类型
   Complex128   // 128复数类型
   Array        // 数值
   Chan         // 通道
   Func         // 函数
   Interface    // 接口
   Map          // 映射
   Pointer      // 指针
   Slice        // 切片
   String       // 字符串
   Struct       //  结构体
   UnsafePointer // 底层指针

)

其中Map,Slice,Chan属于引用类型,使用起来类似于指针,但是在种类常量中仍然属于独立的种类,

   不属于Ptr。type A struct{}定义的结构体属于Struct种类,*A属于Ptr;通常在使用反射来获取变量的类型时,就是用reflect.Kind()来判断所属系统类型

package main

import (
	"fmt"
	"reflect"
)

/*
提示: Go语言不支持解析string然后执行,Go语言的反射机制只能作用在“已经存在的对象”上

		Go语言中的反射的种类(Kind):
	      type Kind uint

const (

	Invalid Kind = iota  // 非法的类型
	Bool        // 布尔型
	Int         // 有符号整形
	Int8        // 有符号8位整形
	Int16       // 有符号16位整形
	Int32       // 有符号32位整形
	Int64       // 有符号64位整形
	Uint        // 无符号整形
	Uint8       // 无符号8位整形
	Uint16      // 无符号16位整形
	Uint32      // 无符号32位整形
	Uint64      // 无符号64位整形
	Float32     // 单精度浮点数
	Float64      // 双精度浮点数
	Complex64    // 64复数类型
	Complex128   // 128复数类型
	Array        // 数值
	Chan         // 通道
	Func         // 函数
	Interface    // 接口
	Map          // 映射
	Pointer      // 指针
	Slice        // 切片
	String       // 字符串
	Struct       //  结构体
	UnsafePointer // 底层指针

)

其中Map,Slice,Chan属于引用类型,使用起来类似于指针,但是在种类常量中仍然属于独立的种类,

	不属于Ptr。type A struct{}定义的结构体属于Struct种类,*A属于Ptr;通常在使用反射来获取变量的类型时,就是用reflect.Kind()来判断所属系统类型
*/
func main() {
	var a int

	typeOfA := reflect.TypeOf(a)
	if typeOfA.Kind() == reflect.Int {
		fmt.Printf("变量a的类型kind是 %s ,kind.type: %s", typeOfA.Name(), typeOfA.Kind())
	} else {
		fmt.Printf("变量a的类型kind是 %s", typeOfA.Kind())
	}

}

运行效果:


GOROOT=D:\program_file_worker\go1.20 #gosetup
GOPATH=D:\program_file_worker\go1.20\bin;C:\Users\Administrator\go #gosetup
D:\program_file_worker\go1.20\bin\go.exe build -o C:\Users\Administrator\AppData\Local\Temp\GoLand\___go_build_OOPReflectToGrammarBaseUse_go.exe D:\program_file\go_workspace\org.jd.data\reflect\OOPReflectToGrammarBaseUse.go #gosetup
C:\Users\Administrator\AppData\Local\Temp\GoLand\___go_build_OOPReflectToGrammarBaseUse_go.exe
变量a的类型kind是 int ,kind.type: int
Process finished with the exit code 0


 

三: go语言反射获取类型信息


package main

import (
	"fmt"
	"reflect"
)

/*
通过反射获取类型信息
*/
func main() {
	println("=============1====================")
	var num Number = 10
	typeOfNum := reflect.TypeOf(num)
	fmt.Printf("typeOfNum")
	checkType(typeOfNum)
	println("=============2====================")
	var person Person
	typeOfPerson := reflect.TypeOf(person)
	fmt.Printf("typeOfPerson")
	checkType(typeOfPerson)
	println("=============3====================")
	typeOfPersonPtr := reflect.TypeOf(&person)
	fmt.Printf("typeOfPersonPtr")
	checkType(typeOfPersonPtr)
}

type Number int

type Person struct {
}

// 定义一个检查数据类型的函数
func checkType(t reflect.Type) {
	if t.Kind() == reflect.Ptr {
		fmt.Printf("变量的类型名称%v ,指向的变量的值为: ", t.Kind())
		t = t.Elem()
	}
	fmt.Printf("变量的类型名称 ==> %v ,类型种类 ==> %v  \n", t.Name(), t.Kind())
}

 运行效果:


GOROOT=D:\program_file_worker\go1.20 #gosetup
GOPATH=D:\program_file_worker\go1.20\bin;C:\Users\Administrator\go #gosetup
D:\program_file_worker\go1.20\bin\go.exe build -o C:\Users\Administrator\AppData\Local\Temp\GoLand\___go_build_org_jd_data_org_jd_data_reflect__1_.exe D:\program_file\go_workspace\org.jd.data\reflect\OOPReflectToGrammarBaseUseFindTypeInfo.go #gosetup
C:\Users\Administrator\AppData\Local\Temp\GoLand\___go_build_org_jd_data_org_jd_data_reflect__1_.exe
=============1====================

Process finished with the exit code 0

typeOfNum变量的类型名称 ==> Number ,类型种类 ==> int
=============2====================
typeOfPerson变量的类型名称 ==> Person ,类型种类 ==> struct
=============3====================
typeOfPersonPtr变量的类型名称ptr ,指向的变量的值为: 变量的类型名称 ==> Person ,类型种类 ==> struct


 

四: go语言反射获取类型的值信息


package main

import (
	"fmt"
	"reflect"
)

/*
通过反射获取类型的值信息
*/
func main() {
	println("================1=====================")
	var num int = 10
	valueOfNum := reflect.ValueOf(num)
	fmt.Println("valueOfNum")
	checkValue(valueOfNum)
	println("================2=====================")
	valueOfNumPtr := reflect.ValueOf(&num)
	fmt.Println("valueOfNumPtr")
	checkValue(valueOfNumPtr)

}

// 定义一个获取数据类型的值的函数
func checkValue(v reflect.Value) {
	if v.Kind() == reflect.Ptr {
		v = v.Elem()
	}
	if v.Kind() == reflect.Int {
		// 方法一
		var v1 = int(v.Int())
		// 方法二
		var v2 int = v.Interface().(int)
		fmt.Println(v1, v2)
	}

}

运行效果:
 


GOROOT=D:\program_file_worker\go1.20 #gosetup
GOPATH=D:\program_file_worker\go1.20\bin;C:\Users\Administrator\go #gosetup
D:\program_file_worker\go1.20\bin\go.exe build -o C:\Users\Administrator\AppData\Local\Temp\GoLand\___go_build_OOPReflectToGrammarBaseUseFindValueInfo_go.exe D:\program_file\go_workspace\org.jd.data\reflect\OOPReflectToGrammarBaseUseFindValueInfo.go #gosetup
C:\Users\Administrator\AppData\Local\Temp\GoLand\___go_build_OOPReflectToGrammarBaseUseFindValueInfo_go.exe
================1=====================
valueOfNum
10 10
================2=====================
valueOfNumPtr
10 10

Process finished with the exit code 0

五: go使用反射动态调用函数


go语言反射动态调用函数
    使用反射调用函数需要用到reflect.ValueOf()方法,传入想要反射的函数名,
    获取到reflect.Value对象,在通过reflect.Value对象的Call方法调用该函数,Call方法的声明如下
        func (v Value) Call(in []Value) []Value
     Call方法使用输入的参数in调用v持有的函数。例如,如果len(in)==3,v.Call(in)代表调用v(in[0],in[1],
     in[2]) (其中Value值表示其持有的值)。如果v的Kind不是Func会引发panic。它返回函数所有输出结果的Value
     封装的切片。和Go代码一样,每一个输入参数的持有值都必须可以直接赋值给函数对应输入参数的类型。如果持有值是可
     变参数函数,Call方法会自行创建一个代表可变参数的切片,将对应可变参数的值都拷贝到里面;

package main

import (
	"fmt"
	"reflect"
)

/*
go语言反射动态调用函数
    使用反射调用函数需要用到reflect.ValueOf()方法,传入想要反射的函数名,
    获取到reflect.Value对象,在通过reflect.Value对象的Call方法调用该函数,Call方法的声明如下
        func (v Value) Call(in []Value) []Value
     Call方法使用输入的参数in调用v持有的函数。例如,如果len(in)==3,v.Call(in)代表调用v(in[0],in[1],
     in[2]) (其中Value值表示其持有的值)。如果v的Kind不是Func会引发panic。它返回函数所有输出结果的Value
     封装的切片。和Go代码一样,每一个输入参数的持有值都必须可以直接赋值给函数对应输入参数的类型。如果持有值是可
     变参数函数,Call方法会自行创建一个代表可变参数的切片,将对应可变参数的值都拷贝到里面;

*/
func main() {
	// 反射调用函数需使用ValueOf
	valueOfFunc := reflect.ValueOf(Equal)

	// 构造函数参数
	args := []reflect.Value{reflect.ValueOf(1), reflect.ValueOf(2)}

	// 通过反射调用函数计算
	result := valueOfFunc.Call(args)

	fmt.Println("函数运行的结果: ", result[0].Bool())
}

func Equal(a, b int) bool {
	if a == b {
		return true
	}
	return false
}

运行效果:


GOROOT=D:\program_file_worker\go1.20 #gosetup
GOPATH=D:\program_file_worker\go1.20\bin;C:\Users\Administrator\go #gosetup
D:\program_file_worker\go1.20\bin\go.exe build -o C:\Users\Administrator\AppData\Local\Temp\GoLand\___go_build_org_jd_data_org_jd_data_reflect__2_.exe D:\program_file\go_workspace\org.jd.data\reflect\OOPReflectToGrammarBaseUseCallFunc.go #gosetup
C:\Users\Administrator\AppData\Local\Temp\GoLand\___go_build_org_jd_data_org_jd_data_reflect__2_.exe
函数运行的结果:  false

Process finished with the exit code 0
 

六: 结构体的反射操作


对结构体的操作:

   反射不仅可以获取普通类型变量的值,还可以获取结构体的成员的类型,成员变量的值以及调用结构体的方法
   获取结构体成员类型
      结构体通过reflect.Type()获取反射类型的变量后,可以调用reflect.Type对象的NumField()方法
    获取结构体成员的变量,调用Field()则可以根据索引返回对应结构体字段的详细信息,具体如下:
    // A StructField describes a single field in a struct.

   type StructField struct {
      Name string         // 字段名
      PkgPath string      // 字段路径
      Type      Type      // 字段反射类型对象
      Tag       StructTag // 字段结构体标签
      Offset    uintptr   // 字段在结构体中的偏移
      Index     []int     // 字段的索引值
      Anonymous bool      // 是否是匿名字段

实例一:反射获取结构体的成员类型

package main

import (
	"fmt"
	"reflect"
)

/*
对结构体的操作:

	反射不仅可以获取普通类型变量的值,还可以获取结构体的成员的类型,成员变量的值以及调用结构体的方法
	获取结构体成员类型
	   结构体通过reflect.Type()获取反射类型的变量后,可以调用reflect.Type对象的NumField()方法
	 获取结构体成员的变量,调用Field()则可以根据索引返回对应结构体字段的详细信息,具体如下:
	 // A StructField describes a single field in a struct.

	type StructField struct {
		Name string         // 字段名
		PkgPath string      // 字段路径
		Type      Type      // 字段反射类型对象
		Tag       StructTag // 字段结构体标签
		Offset    uintptr   // 字段在结构体中的偏移
		Index     []int     // 字段的索引值
		Anonymous bool      // 是否是匿名字段
*/
func main() {
	println("===============1============================\n")
	person := PersonStruct{"老表", 20, "备注"}
	typeOfPersonStruct := reflect.TypeOf(person)

	// 遍历所有结构体成员获取字段信息
	fmt.Println("遍历结构体")
	for i := 0; i < typeOfPersonStruct.NumField(); i++ {
		field := typeOfPersonStruct.Field(i)
		fmt.Printf("字段名: %v   字段标签:  %v 是否是匿名字段: %v  \n",
			field.Name, field.Tag, field.Anonymous)
	}
	println("===============2============================\n")
	println()
	// 通过字段名获取字段信息
	if field, ok := typeOfPersonStruct.FieldByName("Age"); ok {
		fmt.Println("通过字段名")
		fmt.Printf("字段名: %v, 字段标签中json: %v  \n", field.Name, field.Tag.Get("json"))
	}
	println()
	println("===============3============================\n")
	// 通过下标获取字段信息
	field := typeOfPersonStruct.FieldByIndex([]int{1})
	fmt.Println("通过下标")
	fmt.Printf("字段名: %v   字段标签:  %v 是否是匿名字段: %v  \n", field.Name, field.Tag, field.Anonymous)
}

type PersonStruct struct {
	Name   string
	Age    int `json:"age"`
	string     // 匿名字段,只有类型,没有字段名称
}

运行效果:


GOROOT=D:\program_file_worker\go1.20 #gosetup
GOPATH=D:\program_file_worker\go1.20\bin;C:\Users\Administrator\go #gosetup
D:\program_file_worker\go1.20\bin\go.exe build -o C:\Users\Administrator\AppData\Local\Temp\GoLand\___go_build_OOPReflectToGrammarBaseUseStructInfo_go.exe D:\program_file\go_workspace\org.jd.data\reflect\OOPReflectToGrammarBaseUseStructInfo.go #gosetup
C:\Users\Administrator\AppData\Local\Temp\GoLand\___go_build_OOPReflectToGrammarBaseUseStructInfo_go.exe
===============1============================

遍历结构体
字段名: Name   字段标签:   是否是匿名字段: false
字段名: Age   字段标签:  json:"age" 是否是匿名字段: false
字段名: string   字段标签:   是否是匿名字段: true
===============2============================


通过字段名
字段名: Age, 字段标签中json: age

===============3============================

通过下标
字段名: Age   字段标签:  json:"age" 是否是匿名字段: false

Process finished with the exit code 0


实例二:反射获取结构体的成员变量值及调用结构体的方法


package main

import (
	"fmt"
	"reflect"
)

/*
反射获取结构体成员字段的值
*/
func main() {
	println("===================1================================")
	person := PersonValue{Name: "老杨", Age: 27, Address: "北京市海淀区马连洼街道25号院", College: "北京大学"}
	fmt.Printf("打印匿名字段[默认类型初始值]: %d \n", person.int)

	valueOfPersonValue := reflect.ValueOf(person)
	fmt.Printf("person的成员字段数量: %d \n", valueOfPersonValue.NumField())
	// 通过下标访问获取字段值
	fmt.Println("Field")
	field := valueOfPersonValue.Field(1)
	fmt.Println("字段值: ", field.Int())
	println("===================2================================")
	// 通过字段名称获取字段的值
	field = valueOfPersonValue.FieldByName("Age")
	fmt.Println("FieldByName")
	fmt.Printf("字段值: %v \n", field.Interface())

	println("===================3================================")
	// 通过下标获取字段的值
	field = valueOfPersonValue.FieldByIndex([]int{0})
	fmt.Println("FieldByIndex")
	fmt.Printf("字段值: %v \n", field.Interface())
	println("===================4================================")
	methodFindName := valueOfPersonValue.MethodByName("GetName")
	// 执行结构体的方法
	methodFindName.Call([]reflect.Value{}) // 传递一个Value类型的空数据作为函数的参数

}

// PersonValue 当我们定义结构体的时候,字段可以只有类型, 而没有字段名,这样的字段称为匿名字段(Anonymous Field)
type PersonValue struct {
	Name    string
	Age     int    `json:"age"`
	Address string `json:"address"`
	College string
	int     // 匿名字段
}

// GetName 结构体的方法
func (p PersonValue) GetName() {
	fmt.Println(p.Name)
}

运行效果:


GOROOT=D:\program_file_worker\go1.20 #gosetup
GOPATH=D:\program_file_worker\go1.20\bin;C:\Users\Administrator\go #gosetup
D:\program_file_worker\go1.20\bin\go.exe build -o C:\Users\Administrator\AppData\Local\Temp\GoLand\___go_build_OOPReflectToGrammarBaseUseStructValueInfo_go.exe D:\program_file\go_workspace\org.jd.data\reflect\OOPReflectToGrammarBaseUseStructValueInfo.go #gosetup
C:\Users\Administrator\AppData\Local\Temp\GoLand\___go_build_OOPReflectToGrammarBaseUseStructValueInfo_go.exe
===================1================================
打印匿名字段[默认类型初始值]: 0
person的成员字段数量: 5
Field
字段值:  27
===================2================================
FieldByName
字段值: 27
===================3================================
FieldByIndex
字段值: 老杨
===================4================================
老杨

Process finished with the exit code 0
 

七: go语言反射三大定律认知


反射三大定律认知升维:
定律一:反射可以将接口类型的数据变量转换为反射类型的变量
   (1)在使用反射的时候,牢记反射的三大定律会让你对反射有更加清晰的认识
    (2)反射第一定律的体现函数
       2.1 来看下reflect.TypeOf()与reflect.ValueOf()函数的签名:
      func TypeOf(i interface{}) Type {
      }
        func ValueOf(i interface{}) Value {
      }
       2.2 这两个函数的方法类型是reflect.Type和reflect.Value;而这个数据类型称为反射类型
     (3)这两个函数如此都是接口类型变量入参,返回类型为反射类型
定律二:反射可以将反射类型变量转换为接口类型变量
     这里主要使用reflect.Value对象的Interface()方法
定律三:想要使用反射来修改变量的值,其值必须是可写(CanSet)。
   这个值必须满足两个条件:
      一是变量可以被寻址(CanAddr)
      二是变量是可以导出的(结构体字段的首字母需大写)

package main

import (
	"fmt"
	"reflect"
)

/*
反射三大定律认知升维:
定律一:反射可以将接口类型的数据变量转换为反射类型的变量

		(1)在使用反射的时候,牢记反射的三大定律会让你对反射有更加清晰的认识
	    (2)反射第一定律的体现函数
	       2.1 来看下reflect.TypeOf()与reflect.ValueOf()函数的签名:
			func TypeOf(i interface{}) Type {
			}
	        func ValueOf(i interface{}) Value {
			}
	       2.2 这两个函数的方法类型是reflect.Type和reflect.Value;而这个数据类型称为反射类型
	     (3)这两个函数如此都是接口类型变量入参,返回类型为反射类型

定律二:反射可以将反射类型变量转换为接口类型变量

	这里主要使用reflect.Value对象的Interface()方法

定律三:想要使用反射来修改变量的值,其值必须是可写(CanSet)。

	这个值必须满足两个条件:
	   一是变量可以被寻址(CanAddr)
	   二是变量是可以导出的(结构体字段的首字母需大写)
*/
func main() {
	println("========================反射第一定律=============================================\n")
	var a int = 5
	fmt.Printf("type:%T  \n", reflect.TypeOf(a))
	fmt.Printf("value:%T  \n", reflect.ValueOf(a))

	println("========================反射第二定律=============================================\n")

	var b int = 20
	valueOfb := reflect.ValueOf(b)
	fmt.Printf("字段b的值: %d", valueOfb.Interface())

	println("========================反射第三定律=============================================\n")
	animal := Animal{"旺财", 20, "四足爬行类"}
	valueOfAnimal := reflect.ValueOf(&animal)
	typeOfAnimal := reflect.TypeOf(&animal)

	for i := 0; i < valueOfAnimal.Elem().NumField(); i++ {
		fieldValue := valueOfAnimal.Elem().Field(i)
		fieldType := typeOfAnimal.Elem().Field(i)
		fmt.Printf("类型名: %v  可以寻址: %v  可以设置: %v \n",
			fieldType.Name, fieldValue.CanAddr(), valueOfAnimal.CanSet())
	}

	println()
	fmt.Println("修改前: ", animal)
	// 必须满足可寻址和可导出才能修改变量值
	valueOfAnimal.Elem().Field(0).SetString("小咪咪")
	fmt.Println("修改后: ", animal)

}

type Animal struct {
	Name string
	age  int `json:"age"`
	string
}


运行效果:


GOROOT=D:\program_file_worker\go1.20 #gosetup
GOPATH=D:\program_file_worker\go1.20\bin;C:\Users\Administrator\go #gosetup
D:\program_file_worker\go1.20\bin\go.exe build -o C:\Users\Administrator\AppData\Local\Temp\GoLand\___go_build_OOPReflectToGrammarBaseThreeLaws_go.exe D:\program_file\go_workspace\org.jd.data\reflect\OOPReflectToGrammarBaseThreeLaws.go #gosetup
C:\Users\Administrator\AppData\Local\Temp\GoLand\___go_build_OOPReflectToGrammarBaseThreeLaws_go.exe
========================反射第一定律=============================================


type:*reflect.rtype
value:reflect.Value
========================反射第二定律=============================================


字段b的值: 20========================反射第三定律=============================================


类型名: Name  可以寻址: true  可以设置: false
类型名: age  可以寻址: true  可以设置: false
类型名: string  可以寻址: true  可以设置: false

修改前:  {旺财 20 四足爬行类}
修改后:  {小咪咪 20 四足爬行类}

Process finished with the exit code 0

你可能感兴趣的:(golang,开发语言,后端,go语言反射认知升维,1024程序员节)