go反射实战

文章目录

  • [demo1 数据类型判断](#demo1 数据类型判断)
  • [demo2 打印任意类型数据](#demo2 打印任意类型数据)

demo1 数据类型判断

  1. 使用reflect.TypeOf()方法打印go中数据类型,可参考go官方API文档;
  2. 使用格式化参数%T也能打印数据类型。
go 复制代码
package main

import "fmt"
import "reflect"
import "io"
import "os"

func main() {
	TypeTest()
}

func TypeTest() {
	tInt := reflect.TypeOf(3)               // int
	tStr := reflect.TypeOf("文字")           // string
	tBool := reflect.TypeOf(true)            // bool
	tFloat := reflect.TypeOf(3.14)           // float64
	tSlice := reflect.TypeOf([]int{1, 2})    // []int
	tMap := reflect.TypeOf(map[int]string{}) // map[int]string
	var w io.Writer = os.Stdout              // *os.File
	tW := reflect.TypeOf(w)
	fmt.Println(tInt, tStr, tBool, tFloat, tSlice, tMap, tW)
	fmt.Printf("%T %T %T %T %T %T %T", 3, "feng", true, 3.14, []int{1, 2}, map[int]string{}, os.Stdout)
}

输出

int string bool float64 []int map[int]string *os.File
int string bool float64 []int map[int]string *os.File

demo2 打印任意类型数据

开始写代码之前,简单了解一些reflect包中的结构体和方法。

1.结构体:reflect.Value(类型+数据指针)

type Value struct {
	typ *rtype
	ptr unsafe.Pointer
	flag
}
type flag uintptr

2.方法:reflect.ValueOf()

入参:接口interface{},也就是任意类型

出参:reflect.Value结构体

func ValueOf(i any) Value {
	if i == nil {
		return Value{}
	}
	escapes(i)
	return unpackEface(i)
}

3.方法:reflect.Value{}.Interface()

将Value的数据值转为interface{}类型

func (v Value) Interface() (i any) {
	return valueInterface(v, true)
}

4.类型:reflect.Kind

实际上Kind是一个uint类型的别名,使用Kind类型定义了go中各种数据类型,枚举如下

(iota变量是0,常量块定义中使用iota,后面的如果没有指定数值,一般就是自增)

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
)
  1. 结构体:reflect.Type(数据类型)

    type Type interface {
    // 对齐方式
    Align() int

     // 结构体字段的对齐方式
     FieldAlign() int
     
     // 从方法集合中返回索引为i的方法
     Method(int) Method
     
     // 通过方法名在方法集合中找方法,返回方法和是否找到的bool类型结果
     MethodByName(string) (Method, bool)
     
     // 返回方法数量
     NumMethod() int
    
     // 返回类型的名称,例如 int、string等
     Name() string
    
     //  返回包路径,例如"encoding/base64"
     PkgPath() string
    
     // 返回类型大小,比如int占8字节
     Size() uintptr
     
     // 返回最段的类型,例如"base64"而不是"encoding/base64"
     String() string
    
     // 返回类型的数字枚举
     Kind() Kind
     
     // 返回u类型是否实现了接口
     Implements(u Type) bool
    
     // 当前类型的值是否可以赋值为u类型
     AssignableTo(u Type) bool
    
     // 当前类型的值是否可以转换为u类型,就算可转换,依然可能会panic。比如数组大小不匹配时进行转换
     ConvertibleTo(u Type) bool
    
     // 此类型是否可比较,返回true在比较时也可能panic,因为interface是可比较的,但是interface的子类可能是不可比较的
     Comparable() bool
    
     Bits() int
    
     ChanDir() ChanDir
    
     IsVariadic() bool
    
     // 返回此类型的元素类型,必须是Array、Chan、Map、Pointer、Slice类型调用,否则会panic
     Elem() Type
    
     // 返回索引为i的结构体类型的字段
     Field(i int) StructField
    
     // 必须是结构体调用,否则会panic。返回嵌套字段
     FieldByIndex(index []int) StructField
    
     // 根据名字获取字段,找到返回true
     FieldByName(name string) (StructField, bool)
    
     // 根据条件查找字段
     FieldByNameFunc(match func(string) bool) (StructField, bool)
    
     // In returns the type of a function type's i'th input parameter.
     // It panics if the type's Kind is not Func.
     // It panics if i is not in the range [0, NumIn()).
     In(i int) Type
    
     // Key returns a map type's key type.
     // It panics if the type's Kind is not Map.
     Key() Type
    
     // Len returns an array type's length.
     // It panics if the type's Kind is not Array.
     Len() int
    
     // NumField returns a struct type's field count.
     // It panics if the type's Kind is not Struct.
     NumField() int
    
     // NumIn returns a function type's input parameter count.
     // It panics if the type's Kind is not Func.
     NumIn() int
    
     // NumOut returns a function type's output parameter count.
     // It panics if the type's Kind is not Func.
     NumOut() int
    
     // Out returns the type of a function type's i'th output parameter.
     // It panics if the type's Kind is not Func.
     // It panics if i is not in the range [0, NumOut()).
     Out(i int) Type
    
     common() *rtype
     uncommon() *uncommonType
    

    }

了解反射包下的基本数据结构和方法后,下面开始编程

go 复制代码
package main

import (
	"fmt"
	"reflect"
	"strconv"
)

// 任何类型转打印
func AnyToString(a interface{}) string {
	// v是Value类型,属性包含a的实际类型+值
	v := reflect.ValueOf(a)
	// 判断v的类型
	switch v.Kind() {
	case reflect.Invalid: // 无效值
		return "invalid"
	case reflect.String: // 字符串
		return v.String()
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: // 数字类型
		return strconv.FormatInt(v.Int(), 10)
	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: // 无符号数字类型
		return strconv.FormatUint(v.Uint(), 10)
	case reflect.Bool: // 布尔类型
		return strconv.FormatBool(v.Bool())
	case reflect.Float64, reflect.Float32: // 浮点数类型
		return strconv.FormatFloat(v.Float(), 'f', -1, 64)
	case reflect.Ptr: // 指针类型
		if v.IsNil() {
			return "<nil>"
		}
		// v.Elem()取出指针指向的数据,类型为reflect.Value
		// v.Elem().Interface()将reflect.Value转为interface{}
		// AnyToString(v.Elem().Interface()) 递归再次获取字符串
		return AnyToString(v.Elem().Interface())
	case reflect.Slice, reflect.Array: // 切片和数组类型
		s := "["
		// 获取数组或者切片的长度
		length := v.Len()
		for i := 0; i < length; i++ {
			// v.Index(i)为获取下标为i的reflect.Value类型数据
			// v.Index(i).Interface() 将reflect.Value转为interface{}
			// AnyToString(v.Index(i).Interface()) 递归再次获取字符串
			s += AnyToString(v.Index(i).Interface())
			if i < length-1 {
				s += ","
			}
		}

		s += "]"
		return s
	case reflect.Map: // 字典类型
		// 反射获取map的所有key
		keys := v.MapKeys()
		// 获取map的长度
		length := len(keys)
		s := "{"
		for i := 0; i < length; i++ {
			key := keys[i]
			// 获取map的value
			value := v.MapIndex(key)
			s += fmt.Sprintf("%s", AnyToString(key.Interface())) // 拼接key
			s += ": "
			s += AnyToString(value.Interface()) // 拼接value
			if i < length-1 {
				s += ", "
			}
		}
		s += "}"
		return s
	case reflect.Struct: // 结构体类型
		s := "{"
		count := v.NumField() // 获取结构体的字段数量
		for i := 0; i < count; i++ {
			// v.Type().Field(i) 
			s += fmt.Sprintf("%s:%s", v.Type().Field(i).Name, AnyToString(v.Field(i).Interface()))
			if i < count-1 {
				s += ","
			}
		}
		s += "}"
		return s
	default: // 其他类型
		return fmt.Sprintf("%+v", v)
	}
}

func main() {
	fmt.Println(AnyToString(1))
	fmt.Println(AnyToString("字符串"))
	fmt.Println(AnyToString(3.1415926))
	fmt.Println(AnyToString(255))
	fmt.Println(AnyToString([]int{1, 2, 3}))
	fmt.Println(AnyToString(map[string]int{"age": 1}))

	account := &Account{
		Age:  2,
		Name: "jinnian",
	}
	accountList := []Account{
		{1, "wo"},
		{0, "c"},
	}
	fmt.Println(AnyToString(accountList))
	fmt.Println(AnyToString(account))
	fmt.Println(AnyToString(nil))
	fmt.Println(AnyToString(true))
	fmt.Println(AnyToString(&accountList))
}

输出

1
字符串
3.1415926
255
[1,2,3]
{age: 1}
[{Age:1,Name:wo},{Age:0,Name:c}]
{Age:2,Name:jinnian}
invalid
true
[{Age:1,Name:wo},{Age:0,Name:c}]

开始学起来吧

相关推荐
XiaoLeisj39 分钟前
【JavaEE初阶 — 多线程】单例模式 & 指令重排序问题
java·开发语言·java-ee
paopaokaka_luck40 分钟前
【360】基于springboot的志愿服务管理系统
java·spring boot·后端·spring·毕业设计
励志成为嵌入式工程师2 小时前
c语言简单编程练习9
c语言·开发语言·算法·vim
捕鲸叉2 小时前
创建线程时传递参数给线程
开发语言·c++·算法
A charmer2 小时前
【C++】vector 类深度解析:探索动态数组的奥秘
开发语言·c++·算法
码农小旋风2 小时前
详解K8S--声明式API
后端
Peter_chq2 小时前
【操作系统】基于环形队列的生产消费模型
linux·c语言·开发语言·c++·后端
Yaml43 小时前
Spring Boot 与 Vue 共筑二手书籍交易卓越平台
java·spring boot·后端·mysql·spring·vue·二手书籍
小小小妮子~3 小时前
Spring Boot详解:从入门到精通
java·spring boot·后端
hong1616883 小时前
Spring Boot中实现多数据源连接和切换的方案
java·spring boot·后端