反射:
反射是指在程序运行期对程序本身进行访问和修改的能力,即可以在运行时动态获取变量的各种信息。
Go的反射是通过接口的类型信息实现的,即反射建立在接口类型的基础上:当向接口变量赋予一个实体类型的时候,接口会存储实体的类型信息(Type)和值信息(Value)。因此我们可以在运行时利用反射:
- 可以获取变量的类型信息(Type)比如:比如变量的类型信息比如:(type)以及类别(kind),如果是结构体变量,还可以获取到结构体本身的信息(字段与方法)
- 还可以用于获取变量的值信息或者实例信息Value(当变量是一个ptr时)
- 通过反射,还可以修改变量的值,可以调用关联的方法(跟Java中的反射定义基本一样)。
【Tips】:Type是一个定义在reflect包中的interface,而Value是定义在reflect包中的一个struct,Value其实现了Type接口
Go中反射相关的包是reflect,reflect 提供了2个重要函数:
- ValueOf():获取变量的值信息,即pair中的 value
- TypeOf():获取变量的类型信息,即pair中的 concrete type
通过反射获取(接口)变量的类型信息
此时要通过reflect的Type及其相关操作来实现:Type相关主要用于获取一个(接口)变量的类型信息,而不是实例信息(实例信息通过Value来获取)!!!
注意点:
- 如果想访问结构体类型的字段只能通过获取结构体类型变量的基础类型Type这种方式来获取其字段信息,此时如果变量不是指针类型,则可以通过reflect.TypeOf()来获取其concrete type,concrete type即为其基础类型;如果变量是指针类型,则必须还得通过Type.Elem()来获取其基础类型(基础类型也叫基类型)
- 利用反射同样可以访问那些非导出成员
- 利用反射同样可以访问结构体类型的tag信息
- 在获取结构体类型变量对应的结构体类型的方法成员时,要记得区分开指针类型和基础类型,如果方法被声明为func (typ * Type) func1(){…}则,需要通过该变量的指针类型Type来访问对应的方法成员(此时还可以访问那些被声明为基础类型所用拥有的方法也就是func (typ Type) func2(){…}这样的方法),如果方法被声明为func (typ Type) func2(){…},则只能通过该变量的基础类型Type来访问对应的方法成员
这里注意一下类型(Type)与种类(Kind)的区别:
-
Type是原生数据类型: int、string、bool、float32 ,以及 type 定义的类型,对应的反射获取方法是reflect.Type 中 的 Name(),
-
Kind是对象归属的品种:Int、Bool、Float32、Chan、String、Struct、Ptr(指针)、Map、Interface、Fune、Array、Slice、Unsafe
Pointer等
比如:
type Person struct{...}
这里 Person 即为 type
struct 即为其kind
package test
/*
@Author:David Ma
@Date:2020-12-07
@Content:reflect_demo
*/
import (
"../demo"
"fmt"
"reflect"
"testing"
)
func TestReflect(t *testing.T) {
u := new(demo.User)
u.SetPhone("12345678901")
u.SetUser