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

news2024/12/24 11:29:17


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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1129077.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

图像数据增强算法汇总(Python)

一、数据增强概述 数据增强是一种通过使用已有的训练样本数据来生成更多训练数据的方法&#xff0c;可以应用于解决数据不足的问题。数据增强技术可以用来提高模型的泛化能力&#xff0c;减少过拟合现象。比如在狗猫识别项目中&#xff0c;通过随机旋转、翻转和裁剪等数据增强…

什么是CDN?

目录 1.什么是CDN&#xff1f; 2.CDN工作原理 3.为什么要使用CDN&#xff1f; 4.CDN与传统网站访问的区别 5.CDN的应用场景 6.术语解释 1.什么是CDN&#xff1f; CDN英文全称Content Delivery Network&#xff0c;中文翻译即为内容分发网络。它是建立并覆盖在承载网之上…

【带头学C++】 ----- 1.基础知识 ---- 1.20 Typedef 为已有的类型取别名

1.20 Typedef 为已有的类型取别名 1.typedef语法格式 在 C 中&#xff0c;可以使用 typedef 关键字来为已有的类型创建一个别名。这对于提高代码可读性、简化类型声明以及增加代码的可维护性非常有用。 下面是 typedef 的语法格式&#xff1a; typedef 旧类型名 别名; 2.使…

Unity的碰撞检测

&#xff08;一&#xff09;测试前准备工作 1.创建两个游戏对象&#xff0c;分别取名为”Player”和”Enemy”&#xff0c;并且为名为”Player”的游戏对象设置Tag也为”Player”&#xff0c;二者在场景中如图1所示&#xff1a; 图1 绿为Enemy&#xff0c;红为Player 2.编写脚本…

《Linux从练气到飞升》No.22 Linux 进程间通信

&#x1f57a;作者&#xff1a; 主页 我的专栏C语言从0到1探秘C数据结构从0到1探秘Linux菜鸟刷题集 &#x1f618;欢迎关注&#xff1a;&#x1f44d;点赞&#x1f64c;收藏✍️留言 &#x1f3c7;码字不易&#xff0c;你的&#x1f44d;点赞&#x1f64c;收藏❤️关注对我真的…

kafka入门03——简单实战

目录 安装Java 安装Zookeeper 安装Kafka 生产与消费 主要是记录下Kafka的安装配置过程&#xff0c;前置条件需要安装jdk和zookeeper。 安装Java 1.Oracle官网下载对应jdk安装包 官网地址&#xff1a;Java Downloads | Oracle 好人分享了下载需要的oracle账号&#xff0c…

linux基本指令(Chapter 2)

linux基本指令(二) 1.date指令 语法:date [option] [format] date命令的作用是显示时间:使用date %Y:%m:%d可以指定格式显示时间.例如:(注意这里的和%之后不要有任何间隔)并且这里是加号,并不是减号 1.在显示方面&#xff0c;使用者可以设定欲显示的格式&#xff0c;格式设…

NeRF综述

文章目录 《Nerf: Neural radiance field in 3d vision, a comprehensive review 》一、数据集&#xff1a;二、基于方法的分类&#xff08;Method-based Taxonomy&#xff09;三、基于应用的分类&#xff08;Application-based Taxonomy&#xff09;四、未来展望 《Nerf: Neur…

盘点网安最好入手的10大岗位,最高月薪30K!

前段时间&#xff0c;知名机构麦可思研究院发布了《2022年中国本科生就业报告》&#xff0c;其中详细列出近五年的本科绿牌专业&#xff0c;信息安全位列第一。 对于网络安全的发展与就业前景已经说过很多&#xff0c;它是收入较高的岗位之一&#xff0c;在转行领域也占据着热…

番外8.1 Task

---------系统中有两个账号&#xff0c;分别是alex与arod&#xff0c;这两个账号除了支持自己的组&#xff0c;还共同支持一个名为project的组。如这两个账号需要共同拥有/srv/ahome/目录的开发权&#xff0c;且该目录不允许其他账号进入查阅&#xff0c;请问该目录的权限应如何…

动手学深度学习—含并行连结的网络GoogLeNet(代码详解)

目录 1. Inception块3. GoogLeNet模型3. 训练模型 GoogLeNet吸收了NiN中串联网络的思想&#xff0c;并在此基础上做了改进&#xff0c;并且在2014年的ImageNet图像识别挑战赛中获得了不错的效果。 1. Inception块 GoogLeNet论文解决了多大的卷积核最合适的问题。 Inception块…

yolo数据增强,同时旋转txt标签文件

github https://github.com/vkdx/vkdx_cnn-.git YOLO格式txt文件分析 标注好的txt文件中有对应每个标注框的信息,从左到有分别是&#xff1a; class:类别 x_center&#xff1a;标注框中心相对于图像的x坐标 y_center&#xff1a;标注框中心相对于图像的y坐标 w&#xff1a;标…

Day09字符流缓冲流序列化流IO框架

字符流 FileReader&#xff08;文件字符输入流&#xff09; 使用文件字符输入流&#xff0c;有啥好处&#xff1f; 读取中文不会出现乱码问题 FileWriter(文件字符输出流) 利用字符流将一个文本文件复制到E:盘下&#xff0c;例如&#xff1a;D:/1.txt复制到E:/2.txt 请使用…

C语言每日一题(17)数组匹配

牛客网 BC156 牛牛的数组匹配 题目描述 描述 牛牛刚学会数组不久&#xff0c;他拿到两个数组 a 和 b&#xff0c;询问 b 的哪一段连续子数组之和与数组 a 之和最接近。 如果有多个子数组之和同样接近&#xff0c;输出起始点最靠左的数组。 输入描述&#xff1a; 第一行输…

如何在不损失质量的情况下调整图像大小

如何在不损失质量的情况下调整图像大小 如果您在线工作&#xff0c;就会知道图像质量对于呈现干净专业的外观有多么重要。 库存图像和免版税图像很容易找到&#xff0c;但是如何在不损失质量的情况下调整图像大小以使其适合您的目的&#xff1f; 无论您是想将图片用于博客文…

【Java变量】 局部变量、成员变量(类变量,实例变量)、方法参数传递机制

个人简介&#xff1a;Java领域新星创作者&#xff1b;阿里云技术博主、星级博主、专家博主&#xff1b;正在Java学习的路上摸爬滚打&#xff0c;记录学习的过程~ 个人主页&#xff1a;.29.的博客 学习社区&#xff1a;进去逛一逛~ 变量 1. 局部变量与成员变量的区别&#xff1a…

小白必看,手把手教你安装Python

目录 一&#xff0c;Python介绍 二&#xff0c;安装 Python 三&#xff0c;各种疑难杂症&#xff1a; 一&#xff0c;Python介绍 Python 是这两年来比较流行的一门编程语言&#xff0c;主要卖点是其相对简单的语法以及丰富的第三方库&#xff0c;下面我来带大家安装、配置 P…

【七】SpringBoot为什么可以打成 jar包启动

SpringBoot为什么可以打成 jar包启动 简介&#xff1a;庆幸的是夜跑的习惯一直都在坚持&#xff0c;正如现在坚持写博客一样。最开始刚接触springboot的时候就觉得很神奇&#xff0c;当时也去研究了一番&#xff0c;今晚夜跑又想起来了这茬事&#xff0c;于是想着应该可以记录一…

Redis数据类型——list类型介绍及基本操作

1.list类型介绍 redis中的list就是一个双向链表的结构 2.list类型数据基本操作

己知一棵有 2011 个结点的树,其叶结点个数为 116,该树对应的二叉树无右孩子的结点个数是

前言 树转二叉树的规则&#xff1a;每个结点左指针指向它的第一个孩子&#xff0c;右指针指向它在树中相邻的右兄弟&#xff0c;即“左孩子右兄弟“。 拓展&#xff1a;树中一个叶子节点在转化为二叉树的时候&#xff0c;如果它有右兄弟&#xff0c;那么它右指针会指向其兄弟节…