Go 类型断言
sth.(T)
判断某个变量的类型
func main() {
var a interface{}
a = "Hello World"
v, ok := a.(string)
if ok {
fmt.Println(v)
} else {
fmt.Println("a变量非string类型")
}
}
通过switch-case进行多次断言
func getType(x interface{}) {
switch v := x.(type) {
case string:
fmt.Printf("string type,value: %v\n", v)
case int:
fmt.Printf("int type, value: %v\n", v)
case bool:
fmt.Printf("bool type, value: %v\n", v)
default:
fmt.Println("unkown type!")
}
}
反射
反射是什么?
反射是指在程序运行期对程序本身进行访问和修改的能力,比如python当中在调用某个函数的时候,想要获取参数的类型和值等相关信息
golang中的反射
通过reflect内置包实现反射
获取类型 reflect.TypeOf()
func getType(x interface{}) {
t := reflect.TypeOf(x)
fmt.Printf("type:%v\n", t)
}
func main() {
var a float32 = 0.99
getType(a) // type:float32
var b int64 = 123
getType(b) // type:int64
}
对于获取到的类型t,还可以划分为:
- 类型名, t.Name()
对于 数组、切片、Map、指针等类型的变量,通过.Name()获取到的都是空 - 类型种类,t.Kind(),指底层数据结构的种类
package main
import (
"fmt"
"reflect"
)
// 自定义myInt类型
type myInt int64
func reflectType(x interface{}) {
t := reflect.TypeOf(x)
fmt.Printf("typeName:%v typeKind:%v\n", t.Name(), t.Kind())
}
func main() {
var a *float32 // 指针类型
var b myInt // 自定义类型
var c rune // 类型别名
reflectType(a) // typeName: typeKind:ptr
reflectType(b) // typeName:myInt typeKind:int64
reflectType(c) // typeName:int32 typeKind:int32
type student struct {
name string
gender string
}
type company struct {
title string
address string
}
var zhangshan = student{
name: "张三",
gender: "male",
}
var industry = company{title: "Google Inc", address: "No.10086, 5th Ave, NY city, USA"}
reflectType(zhangshan) // type:student kind:struct
reflectType(industry) // type:company kind:struct
}
获取值 reflect.ValueOf()
reflect包获取到的值为原始值,若想要指定类型的值,需要对获取到的原始值进行强转。查看其提供的原始值类型,可进到reflect包内进行查看
- 对于reflect.ValueOf()获取到的v, 通过.Kind()方式拿到值的类型
func reflectValue(x interface{}) {
v := reflect.ValueOf(x)
switch v.Kind() {
case reflect.Int64:
// v.Int()从反射中获取整型的原始值,然后通过int64()强制类型转换
fmt.Printf("type is int64, value is %d\n", int64(v.Int()))
case reflect.Float32:
// v.Float()从反射中获取浮点型的原始值,然后通过float32()强制类型转换
fmt.Printf("type is float32, value is %f\n", float32(v.Float()))
case reflect.Float64:
// v.Float()从反射中获取浮点型的原始值,然后通过float64()强制类型转换
fmt.Printf("type is float64, value is %f\n", float64(v.Float()))
}
}
通过反射来修改值
函数参数传递的是值拷贝,必须传递变量地址才能修改变量值,否则引发panic异常
package main
import (
"fmt"
"reflect"
)
// 传入值类型的变量
func resetValue1(x interface{}) {
v := reflect.ValueOf(x)
if v.Kind() == reflect.Int64 {
v.SetInt(200) // 修改的是副本,reflect包会引发panic
}
}
// 传入指针类型的变量
func resetValue2(x interface{}) {
v := reflect.ValueOf(x)
// 反射中使用 v.Elem()方法获取指针对应的值
if v.Elem().Kind() == reflect.Int64 {
v.Elem().SetInt(200)
}
}
func main() {
var a int64 = 123
resetValue2(&a)
fmt.Println(a)
}
// 仅对通道、函数、接口、映射、指针、切片有效,否则异常
// 判断值是否为nil,v.IsNil() 常用于判断指针是否为空
// 判断是否有值, 如果v是Value零值会返回假
// v.IsValid()
对反射的值进行判断
func main() {
// *int类型空指针
var a *int
fmt.Println("var a *int IsNil:", reflect.ValueOf(a).IsNil())
// nil值
fmt.Println("nil IsValid:", reflect.ValueOf(nil).IsValid())
// 匿名结构体,判断字段或方法是否存在
b := struct{}{}
fmt.Println("不存在的结构体成员:", reflect.ValueOf(b).FieldByName("abc").IsValid())
// 尝试从结构体中查找"abc"方法
fmt.Println("不存在的结构体方法:", reflect.ValueOf(b).MethodByName("abc").IsValid())
// map 判断 key是否存在
c := map[string]int{}
fmt.Println("map中不存在的键:", reflect.ValueOf(c).MapIndex(reflect.ValueOf("娜扎")).IsValid())
}
结构体反射
type student struct {
Name string `json:"name"`
Score int `json:"score"`
}
func main() {
stu1 := student{
Name: "张三",
Score: 66,
}
t := reflect.TypeOf(stu1)
fmt.Println(t.Name(), t.Kind()) // student struct
// 通过索引获取Field
for i := 0; i < t.NumField(); i++ {
field := t.Field(i)
fmt.Printf("name:%s index:%d type:%v json tag:%v\n", field.Name, field.Index, field.Type, field.Tag.Get("json"))
}
// 通过字段名获取Field
if scoreField, ok := t.FieldByName("Score"); ok {
fmt.Printf("name:%s index:%d type:%v json tag:%v\n", scoreField.Name, scoreField.Index, scoreField.Type, scoreField.Tag.Get("json"))
}
}
结构体方法的反射
func printMethod(x interface{}) {
t := reflect.TypeOf(x)
v := reflect.ValueOf(x)
fmt.Println(t.NumMethod())
for i := 0; i < v.NumMethod(); i++ {
methodType := v.Method(i).Type()
fmt.Printf("method name:%s\n", t.Method(i).Name)
fmt.Printf("method:%s\n", methodType)
// 通过反射调用方法传递的参数必须是 []reflect.Value 类型
var args = []reflect.Value{}
v.Method(i).Call(args)
}
}