GO语言-反射【GO学习之路】

267 阅读4分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第30天,点击查看活动详情

前言

本篇来学习Go语言的反射知识。无疑会与Java 中的反射来对比,这样也更易记住。回顾反射的概念,通常就是程序在运行时动态获取变量和方法等信息,下面开始详细来介绍Go语言中的反射知识。

本文大纲

GO语言-反射.png

1. 何为反射

反射是指在运行时动态获取变量名称,类型还有对象的常用方法。这样就可以知道通过反射的方式可以修改程序中对象的变量值以及调用方法。

反射的两个常见使用场景:

  • 若函数的参数类型未知:参数没有确定好、参数类型多没确定下来,那此时类型不能统一表示,需要反射
  • 无法确定调用哪个函数的情形:如根据用户输入来决定调用特定方法,那就需要根据方法名、参数进行反射处理,在运行期间动态执行目标方法

Go中的反射无法获取到一个可执行文件中或者是一个包中的所有类信息,需要配合使用标准库中对应的语法解析器和抽象语法树( AST) 对源码进行扫描来获得信息。

有两点需要注意:

  • 在编译期间,无法对反射代码进行错误提示
  • 过多使用反射会影响性能,要注意使用度

2. 反射的实现

反射建立在类型的基础上,通过类型信息实现。给接口变量赋予类型时,接口会存储类型信息。

Go中与反射相关的标准库是reflect,在该库中,定义了类型,反射的各种函数。
变量包括type、value两个部分。type包括两部分:

  • 静态类型static type:即开发时使用的类型,像 int、string
  • concrete type:是runtime系统使用的类型

在Go语言,反射与interface类型相关,其type是 concrete type,只有interface才有反射!每个interface变量都有一个对应的pair,pair中记录了变量的实际值和类型(value, type)

例如:

var r io.Reader				// 定义Reader类型的变量
var w io.Writer				// 定义Writer类型的变量
w = r.(io.Writer)			// 赋值时,接口内部的pair不变,所以 w 和 r 是同一类型

3. Go语言中使用反射的示例

3.1 reflect中两个常用函数

reflect 提供常用函数:

  • ValueOf():获取变量的值,即pair中的 value
  • TypeOf():获取变量的类型,即pair中的 concrete type
type Person struct {
    Name string
    Age int
}   // Person结构体

p := Person{ "lisi", 13}

fmt.Println(reflect.ValueOf(p))		// {lisi 13}  变量的值
fmt.Println(reflect.TypeOf(p).Name())	// Person:变量类型对象的类型名
fmt.Println(reflect.TypeOf(p).Name() == "Person")	// true

3.2 静态类型与动态类型

静态类型:变量声明时候赋予的类型

type MyInt int			// int 是静态类型

动态类型:运行时给这个变量赋值时,这个值的类型即为动态类型(为nil时没有动态类型)

var A interface{}		// 空接口 是静态类型,必须是接口类型才能实现类型动态变化
A = 10				// 此时静态类型为 interface{} 动态为int
A = "hello"			// 此时静态类型为 interface{} 动态为string

3.3 使用反射调函数

反射值对象(reflect.Value)中值的类型为函数时,可通过 reflect.Value调用函数。
使用反射调用函数时,需要将参数使用反射值对象的切片 reflect.Value 构造后传入 Call()方法中
调用完成时,函数的返回值通过 []reflect.Value 返回

// 声明一个函数
func add(name string, age int) {
    fmt.Printf("name is %s, age is %d \n", name, age)
}

func main() {
    funcValue := reflect.ValueOf(add)  // 通过 reflect.Value调用函数
    params := []reflect.Value{reflect.ValueOf("lisi"), reflect.ValueOf(20)}

    reList := funcValue.Call(params)  // 参数传入 Call()方法
    fmt.Println(reList)		// 函数返回值
}

3.4 反射调用方法

// 声明user 结构体 
type user struct {
    Name string
    Age int
}

// 展示名字
func (u *user) ShowName() {
    fmt.Println(u.Name)
}

func main() {
    u := &user{"lisi", 20}
    v := reflect.ValueOf(u)

    // 调用方法
    methodV := v.MethodByName("ShowName")
}

总结

这篇主要介绍Go语言中的反射知识,反射的定义和使用,通过示例更为直观介绍Go语言反射的使用方式以及语法规则。

掘金(juejin.cn)  一起分享知识,Keep Learning!