Go-类型断言&反射

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)
    }
}
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容