前言
在学习反射的时候,对reflect包中获取变量类型的函数很迷惑
比如下面这个 用Type获取变量类型的方法(在下面提到)
所以结合多方资料进行了学习,并整理了这篇博客
获取变量类型
一、fmt.Printf
直接使用 fmt.Printf 的 %T 打印变量的类型
fmt.Printf("%T\n", 1) //int
fmt.Printf("%T\n", "abc") //string
fmt.Printf("%T\n", true) //bool
fmt.Printf("%T\n", 1.1) //float64
二、类型断言
类型断言是Go语言中用于检查接口值的底层具体类型或将其转换为特定类型的机制。它允许从接口值中提取出具体的值,或者检查接口值是否持有特定的类型
- 基本语法
类型断言有两种形式:
- 单返回值形式:x.T( )
- 双返回值形式:value, ok := x.T( )
- 单返回值形式
value := x.(T)
- 参数:
- x:必须是一个接口类型的表达式
- T:要断言的类型(可以是具体类型或另一个接口类型)
- 返回值:
- 如果 x 是 T 类型,则返回 x 的值
- 如果 x 不是 T 类型,则断言失败,会引发 panic
*** 示例:
断言成功:
a := 1
v := interface{}(a)
value := v.(int) //断言成功
fmt.Println(value) //输出1
断言失败:
a := 1
v := interface{}(a)
value := v.(float64) //断言失败,引发panic
fmt.Println(value)
panic: interface conversion: interface {} is int, not float64
- 双返回值形式
value, ok := x.(T)
参数:
同单返回值形式返回值:
- value:如果断言成功,返回 x 的值(实际上这里存在一个类型转换,x是接口类型的变量,首先将值从interface{ }类型转换为T类型,再赋值给value);否则返回 T 类型的零值
- ok:bool值,如果断言成功是true,断言失败是false
示例:
value, ok := v.(string) //断言失败
fmt.Println(value, ok) //输出“ false” 这里value不是没有输出,而是因为value是string的零值 输出空字符串
fmt.Printf("%T\n", value) //输出“string”--表明value是string类型的零值
value1, ok1 := v.(int) //断言成功
fmt.Println(value1, ok1) //输出“1 true”
- 忽略返回值的规则
- 忽略ok(只关心值)
value, _ := x.(T) //忽略ok
风险:如果断言失败,value是T类型的零值,可能引发逻辑错误
- 忽略value (只检查类型)
如果只关心类型是否匹配,不关心具体值:
_,ok := x.(T) //忽略value
三、类型选择
类型选择是Go语言中一种特殊的 switch 语句,用于检查接口值的动态类型。
必须和switch一起使用
- 基本语法:
switch v := x.(type) {
case T1:
//v的类型是T1
case T2:
//v的类型是T2
default:
}
- 参数
必须是接口类型
*** 示例:
a := 1
v := interface{}(a)
switch v.(type) {
case int:
fmt.Println("int类型")
case string:
fmt.Println("是string类型")
default:
fmt.Println("未知")
}
输出
int
四、反射 reflect.TypeOf
reflect.TypeOf 是Go语言标准库中 reflect 包提供的核心函数之一,用于获取任意值的类型信息
- 基本语法:
// TypeOf returns the reflection [Type] that represents the dynamic type of i.
// If i is a nil interface value, TypeOf returns nil.
func TypeOf(i any) Type {
return toType(abi.TypeOf(i))
}
//TypeOf 返回表示 i 的动态类型的反射 [Type]。
//如果 i 是 nil 接口值,则 TypeOf 返回 nil。
- 参数
接收参数:
接收任意类型的值(实际上存在类型转换,在底层实现原理提到)参数传递机制:
如果是值类型(如int、struct 等),会创建值的副本
如果是引用类型(如slice、map、pointer等),会复制引用
- 返回值
- 返回值类型: reflect.Type(接口类型)
- 返回值内容:
包含动态类型信息
可以获取类型名称、种类(Kind)、方法集等
- 底层实现原理:
func TypeOf(i interface{}) Type
- 调用TypeOf(x) 时:
编译器将x转换为接口值(包含类型指针和值指针)
运行时从接口值中提取类型信息
返回指向内部类型结构的 reflect.Type 接口 - 类型描述符:
Go运行时为每种类型维护一个类型描述符(rtype)
*** 示例:
- 对于基本类型,
a := "bcd"
fmt.Println(reflect.TypeOf(a)) //输出:string
- 对于结构体变量,
type Person struct {
Name string
Age int
}
func main() {
p := Person{
"haha",
18,
}
t := reflect.TypeOf(p)
fmt.Println(t)
//遍历结构体中所有字段
for i := 0; i < t.NumField(); i++ { //NumField计算结构体字段个数
field := t.Field(i) //每个字段的类型和值
fmt.Println(field.Name, field.Type) //field.Type和field.Kind在下面提到
}
}
输出如下
main.Person
Name string
Age int
field.Name 和 field.Type
在Go语言的反射机制中,
field.Name 和 field.Type 是用于描述结构体字段的两个关键属性,
field.Name获取字段名,field.Type获取字段类型
五、reflect.Value的Type()方法
当已经有一个reflect.Value时,可以通过它的Type()方法获取类型
参数必须是Value类型
*** 示例:
v := reflect.ValueOf(3)
t := v.Type() //从Value获取Type
fmt.Println(t) //输出“int”
reflect.TypeOf(x) 和 v.Type() 的区别
- 接收参数
reflect.TypeOf(x) :参数可以为任意类型
v.Type() :参数为Value类型 - 底层实现
reflect.TypeOf(x)内部会先创建一个interface{},然后提取类型信息
v.Type()只是返回reflect.Value内部保存的类型信息