golang 反射

转自:http://golanghome.com/post/546

自己在用Go写Web框架时,遇到要从接口中返回对象信息的技术问题。网上关于Go中接口反射的资料较少,所以自己学习了一段时间,特将结果与大家分享。

代码约定

import (
    "fmt"
    "reflect"
)

type boy struct {
    Name string
    age  int
}

type human interface {
    SayName()
    SayAge()
}

func (this *boy) SayName() {
    fmt.Println(this.Name)
}

func (this *boy) SayAge() {
    fmt.Println(this.age)
}

func main() {
    // 定义接口变量
    var i human
    // 初始化对象,jown持有对象指针。
    jown := &boy{
        Name: "jown",
        age: 15,
    }
    // 因为boy实现了human中的方法,所以它实现了human接口。
    // 这时,i就指向jown对象。
    i = jown 

    // 通过反射获取接口i 的类型和所持有的值。
    t := reflect.TypeOf(i)
    v := reflect.ValueOf(i)

    // ... 后续操作
}

t,v 的打印结果:

tv 实现了String() string 所对应的接口,所以可以在fmt中打印出来。

fmt.Println(t)
fmt.Println(v)

// 打印结果
*main.boy
<*main.boy Value>

从上面的打印结果可见:

  1. t 表示i接口的类型为指向main包下struct boy的指针类型。它可以用来存储指向boy的指针。
  2. v 表示i接口目前的所存储值为指向main包下struct boy的指针,也可以理解为上面代码中的jown

得到了这些信息,我们就可以进行后续操作:

通过接口i查询对象的名字

reflect.Type类型下有一个方法Name() string,用来返回不包含包名的类型的名字。所以,我们需要知道i所存储的对象的类型。reflect.Elem() Type可以返回类型的成员类型。这样我们就可以用这两个函数返回对象的名字。

    // 获取i所指向的对象的类型
    structType := t.Elem()
    // 获取对象的名字
    structName := structType.Name()
    fmt.Println(structName)

    // 打印结果
    boy

通过接口i查询对象的方法信息

由于在Go中,func也是一种类型,所以对象的方法存在值和类型的说法。

通过t获取对象方法的信息。

利用t的MethodByName() Method方法:

    method, _ := t.MethodByName("SayAge")
    fmt.Println(method)

    // 打印结果
    {SayAge  func(*main.boy) <func(*main.boy) Value> 1}

它返回了一个对象方法的信息集合即Method,关于Method结构定义在reflect/type.go下具体为:

// Method represents a single method.
type Method struct {
    // Name is the method name.
    // PkgPath is the package path that qualifies a lower case (unexported)
    // method name.  It is empty for upper case (exported) method names.
    // The combination of PkgPath and Name uniquely identifies a method
    // in a method set.
    // See http://golang.org/ref/spec#Uniqueness_of_identifiers
    Name    string
    PkgPath string

    Type  Type  // method type
    Func  Value // func with receiver as first argument
    Index int   // index for Type.Method
}

可以看出该信息包含对象方法的名字,包路径(导出的方法,此路径为空),方法的类型,方法的接收者,该方法在对象中的位置从0开始。

通过v获取对象方法的信息。

利用v.MethodByName() Value方法:

    method := v.MethodByName("SayAge")
    fmt.Println(method)

    // 打印结果
    <*main.boy Value>

它又返回了一个Value类型。我们通过类型中的方法获取一些对象方法的信息:

    // 返回方法的地址
    fmt.Println(method.Pointer())
    // 返回方法的类型
    fmt.Println(method.Type())
    // 对象方法是否可以更改
    fmt.Println(method.CanSet())

    //打印结果
    4527472
        func()
    false

通过v调用方法。

注意,调用的方法必须是可导出的。

    // 有输入参数的方法调用。例如, func (this *boy) SayName (name string) {}。
    // 构造输入参数
    args := []reflect.Value{reflect.ValueOf("liming")}
    // 通过v进行调用
    v.MethodByName("SayName").Call(args)

    // 无输入参数的方法调用。例如,func (this *boy) SayName(){}。
    // 构造zero value
    args := make([]reflect.Value, 0)
    // 通过v进行调用
    v.MethodByName("SayName").Call(args)

总结

通过上面的代码,Go的反射比较方便和强大。在静态类型语言中实现反射是一件挺伟大的事。当然,这也是得益于Go有一层中间件Runtime,reflect包的大部分功能靠它来实现。上面只是列举了通过接口反射对象的方法信息以及调用方法。我们还可以加以变通来反射对象的字段信息,已经动态修改字段内容等等。总的来说,运用好reflect包对写通用型框架还是很有帮助的。在此只是抛砖引玉,还是望大神Carry,指导。

时间: 2024-08-30 14:47:53

golang 反射的相关文章

golang反射初试

golang反射来自Go AST(Abstract Syntax Tree). reflect操作更多像traverse AST. t := reflect.TypeOf(obj) 使用TypeOf()获取类型信息. v := reflect.ValueOf(obj) 使用ValueOf获取值信息 如果t是Array, Slice, Map, Chan或Pointer,可以继续继续使用Elem()方法获取其元素的更多信息. Elem(): 必须是Array, Slice, Map, Chan或

golang 反射应用(二)

golang反射应用(二) package test import ( "reflect" "testing" ) //定义适配器 func TestReflect(t *testing.T){ //声明回调函数 call1 := func(v1,v2 int){ t.Log(v1,v2) //1 2 } call2 := func(v1,v2 int,s string){ t.Log(v1,v2,s) //1 2 test2 } //定义全局变量 var ( fu

[golang]反射的用处--代码自动生成

背景: go语言处理db.json的时候,具体代码的变量定义和db字段.json输出的时候可能不一样. 这个时候,我们需要用tag的方式来进行定义. 例如: type MyStruct struct { Name string `json:"name"` MaxHeight int `json:"max_height"` } 如果字段或结构体非常多的话,有十几二十几个,就非常的麻烦. 因此,就利用golang的反射,做了一个小工具来自动生成,具体使用如下: //你的

GOLANG 反射法则

译自[blog.golang.org/laws-of-reflection] 在计算机中, 反射是程序通过类型,检测到它自己的结构能力:是一种元编程程:也是一个具大的混淆点 在本文中,我们将通过解释反射是如何在GO中工作的来澄清它.每个语言的反射模式是不同的.本文着重于GO,所以后文中的反射都是指GO中的反射 1.类型和接口因为反射是修建于类型系统之上, 所以让我们从GO的类型开始讲吧.GO是静态类型语言. 每个变量都有一个静态类型. 也就是说, 每一个已经类型在编译时已经固定了其类型:int,

golang 反射解惑

Type和Kind的区别 直接看例子: type Myint int type Person struct { } func main() { var myint Myint= 1 var person Person= Person{} s := 1 var intPtr =&s mySlice := []string{} myMap := map[string]int{} myintType := reflect.TypeOf(myint) personType := reflect.Type

GO开发[六]:golang反射(reflect)

反射 反射:可以在运行时动态获取变量的相关信息 ? Import ("reflect") reflect.TypeOf,获取变量的类型,返回reflect.Type类型 reflect.ValueOf,获取变量的值,返回reflect.Value类型 reflect.Value.Kind,获取变量的类别,返回一个常量 reflect.Value.Interface(),转换成interface{}类型 获取变量的值: reflect.ValueOf(x).Float() reflect

golang反射

反射reflection 反射可大大提高程序的灵活性,使得interface{}有更大的发挥余地 反射使用TypeOf和ValueOf函数从接口中获取目标对象信息 反射会将匿名字段作为独立字段(匿名字段本质) 想要利用反射修改对象状态,前提是interface.data是settable,即pointer-interface 通过反射可以"动态"调用方法 对某一个struct进行反射的基本操作 package main import ( "fmt" "re

Golang-interface(四 反射)

github:https://github.com/ZhangzheBJUT/blog/blob/master/reflect.md 一 反射的规则 反射是程序运行时检查其所拥有的结构,尤其是类型的一种能力:这是元编程的一种形式.它同时也是造成混淆的重要来源. 每个语言的反射模型都不同(同时许多语言根本不支持反射).本节将试图明确解释在 Go 中的反射是如何工作的. 1. 从接口值到反射对象的反射 在基本的层面上,反射只是一个检查存储在接口变量中的类型和值的算法.在 reflect 包中有两个类

【GoLang】golang 如何像Java 一样通过类名反射对象?

结论: golang不支持解析string然后执行. golang的反射机制只能存在于已经存在的对象上面. 不知道后续的版本有没有规划,现在只能先加载注册,然后实现类似Java工厂模式的反射. 代码示例: t := reflect.ValueOf(Human{}).Type() // h := reflect.New(t).Elem() // new return address pointer h := reflect.New(t).Interface() fmt.Println(h) hh