[Go] 反射 - reflect.ValueOf()
类型 和 接口
由于反射是基于类型系统(type system)的,所以先简单了解一下类型系统。
首先 Golang 是一种静态类型的语言,在编译时每一个变量都有一个类型对应,例如:int, floate32, []byte, *MyType 等等。如果我们这样声明:
type MyInt int var i int
var j MyInt
上面的 i 是 int 类型的, j 是 MyInt 类型的。i 和 j 是不同的静态类型,尽管他们都有相同的相关类型(这里就是 int),他们不能互相赋值除非通过强制转换。
一种非常重要的类型分类是接口类型,接口代表中方法的集合。只要一个值实现了接口定义的方法,那么这个值就可以存储这个具体的值。一个著名的例子就是 io 包中的 Reader 和 Writer。
// Reader is the interface that wraps the basic Read method
type Reader interface {
Read(p []byte) (n int, err error)
} // Writer is the interface that wraps the basic Write method
type Writer interface {
Write(p []byte) (n int, err error)
}
任何是实现了 Read (或 Write )方法的签名的类型就是实现了 io.Reader (或者 io.Writer)。也就是说一个 io.Reader 的变量可以持有任何实现了 Read 方法的值。
var r io.Reader
r = os.Stdin
r = bufio.NewReader(r)
r = new(bytes.Buffer)
// and so on
我们要非常清楚的知道不管 r 持有了哪种具体的值,r 的类型永远都是 io.Reader。
一个非常重要的的例子就是一个空的接口:
interface{}
这个代表一个空的方法集合并且满足任何值,只要这个值有零个或者多个方法。
有人说 Golang 中的 interface 是动态类型的,这个一个误导。一个 interface 类型的变量拥有相同的静态类型,尽管运行时这个变量的值会发生改变,但是都是满足一直都是满足这个 interface 的。
##interface的表示
Russ Cox 曾经写个 一篇博文 详细讨论了 Golang 中的 interface 的值。 简单类说,一个 interface 的值存储了一个赋给变量的 具体值 和 这个值类型的 描述。
var r io.Reader
tty,err := os.OpenFile("/dev/tty", os.O_RDWR, 0)
if err != nil {
return nil, err
}
r = tty
这个具体的例子中,r 包含了一个 (value, type) 对,具体的就是(tty, *os.File)。*os.File 实现了 Read 等很多方法,但是 io.Reader 的接口之允许访问 Read 方法,所以我们还可以这样做:
var w io.Writer
w = r.(io.Writer)
通过类型断言 (type assertion),因为 r 照样实现了 io.Writer,所以我们可以将 r 赋值给 w。
Relection goes from interface value to reflection object
本质上来说,反射就是一种检查接口变量的类型和值的机制。最基本的我们要知道 reflect.Type 和 reflect.Value。可以通过 reflect.TypeOf 和 reflect.ValueOf 来得到接口变量的 Type 和 Value,同样可以通过 reflect.Value 轻松得到 reflect.Type。
package main import (
"fmt"
"reflect"
) func main() {
var x float = 3.14
fmt.Println("type:",reflect.TpyeOf(x))
}
结果是:
type: float64
同样可以通过 reflect.ValueOf 轻松得到 Value:
var x float64 = 3.14
fmt.Println("vlaue:", reflect.ValueOf(x))
结果是:
value: <float64 Value>
reflect.Value 和reflect.Type 有很多方法可以供我们使用,具体可以查看 API,例如:
var x float64 = 3.14
v := reflect.ValueOf(x)
fmt.Println("type:",v.Type())
fmt.Println("kind is floate64:",v.Kind() == reflect.Float64)
fmt.Println("value:",v.Float())
结果:
type: float64
kind is float64: true
value: 3.14
1、大的数据类型可以包含小的数据类型,例如 int64 可以是任意的整数 (int8, uint8, int32 等),但是需要转换一下。
var x uint8 = 'x'
v := reflect.ValueOf(x)
fmt.Println("type:",v.Type())
fmt.Println("kind is uint8:", v.Kind() == reflect.Uint8)
x = uint8(v.Uint())
2、如果 Kind() 方法是描述相关的类型,而不是静态的类型,例如用户自定义了一个类型:
type MyInt int
var x MyInt = 7
v := reflect.ValueOf(x)
那么 v.Kind() 的返回值是 reflect.Int,尽管 x 的静态类型是 MyInt 而不是 int。Kind() 无法描述一个 MyInt 的 int 但是 Type 可以。
Relection goe s from reflection object to interface value
与上面相反,从 reflect object 到 interface value 则是非常容易的。只要通过 Value 的 Interface 方面法就可以获得 interface value 了。
y := v.Interface().(float64) // y will have type float64
println(y)
因为 fmt.Println()、fmt.Printf() 接受空接口的值 (empty interface) 作为参数,我们可以这样:
fmt.Println(v.Interface())
fmt.Printf("value is %7.1e\n", v.Interface()) // print: 3.1e+00
To modify a reflection object,the value must be settable
上面我们知道了 interface value 和 reflection object 之间的反射,那么我们如何改变一个 reflection object 呢?
我们是否可以通过 Value 的 SetXXX() 方法来实现呢,就像下面一样:
var x float64 = 3.14
v := reflect.ValueOf(x)
v.SetFloat(2.8)
如果你实验了以上代码,你就会发现在 SetFloat() 的时候会 panic,那么为什么呢?
原因和简单,就是因为 v 不可以被 Set,如何知道一个 Value 是否可以被 Set 呢?通过 CanSet() 方法就可以了。
var x float64 = 3.14
v := reflect.ValueOf(x)
fmt.Println("settability of v:",v.CanSet()) // prints: settability of v: false
可以不可以被 Set 是通过 reflection object 是否持有原始的变量值,例如这样:
var x float64 = 3.14
v := reflect.ValueOf(x)
这段代码中传给 reflect.ValueOf 的是 x 的一个副本,而不是 x 本身,那么 v 就是不可以被 Set 的,所以通过 SetFloat() 方法是不被允许的。那么如何才能被允许被 Set 呢,也许有人会想到了对于函数,我们可以通过传给函数一个指向参数的指针来达到修改参数本身的作用,同样的道理,这里也可以通过传指针:
var x float64 = 3.14
p := reflect.ValueOf(&x) // Note: take the address of x.
fmt.Println("type of p:", p.Type())
fmt.Println("settability of p:", p.CanSet())
结果:
type of p: *float64
settability of p: false
我们可以看到 p 的类型是 *float64,而不是 float64 了,但是为什么还是不可以被 Set 呢,因为这里 p 是一个指针,我们并不是要 Set 这个指针的值,而是要 Set 指针所指内容的值(也就是 *p),所以这里 p 仍然是不可被 Set 的,我们可以通过 Value 的 Elem() 方法来指针所指向内容的 Value:
v := p.Elem()
fmt.Println("settability of v:",v.CanSet()) // prints: settabiliyty of v : true
这个时候我们就可以调用 Value 的 Set 方法:
v.SetFloat(2.8)
fmt.Println(v.Interface()) // prints: 2.8
fmt.Println(x) // prints: 2.8
好了,那么对于一个 Struct 如何来反射呢?我相信你看了这个例子应该就会如何使用了:
type T struct {
A int
B string
} t := T{23,"hello world"}
s := reflect.ValueOf(&t).Elem()
typeOfT := s.Type()
for i:=0; i<s.NumField(); i++ {
f := s.Field(i)
fmt.Printf("%d: %s %s = %v\n", i,typeOfT.Field(i).Name, f.Type(), f.Interface())
}
结果是:
0: A int = 23
1: B string = hello world
同样可以通过以下类似的代码来修改 T 的值:
s.Field(0).SetInt(22)
s.Field(1).SetString("XXOO")
好了,反射就基本如此吧,记住三点即可:
1. Reflection goes from interface value to reflection Object.
2. Reflection goes from refelction object to interface value.
3. To modify a reflection object, the value must be settable.
延伸阅读:
Go - 反射中 函数 和 方法 的调用 - v.Call()
参考:
http://golang.org/doc/articles/laws_of_reflection.html
http://blog.csdn.net/wowzai/article/details/9305147
[Go] 反射 - reflect.ValueOf()的更多相关文章
- Go语言反射reflect
目录 通过反射获取类型信息 理解反射的类型(Type)与种类(Kind) reflect.Elem() - 通过反射获取指针指向的元素类型 通过反射获取结构体的成员类型 通过反射获取值信息 使用反射值 ...
- go语言中的反射reflect
package main; import ( "fmt" "reflect" ) //反射refection //反射使用TypeOf和ValueOf函数从接口 ...
- Go语言学习笔记(四)结构体struct & 接口Interface & 反射reflect
加 Golang学习 QQ群共同学习进步成家立业工作 ^-^ 群号:96933959 结构体struct struct 用来自定义复杂数据结构,可以包含多个字段(属性),可以嵌套: go中的struc ...
- go语言之行--接口(interface)、反射(reflect)详解
一.interface简介 interface(接口)是golang最重要的特性之一,Interface类型可以定义一组方法,但是这些不需要实现.并且interface不能包含任何变量. 简单的说: ...
- Go中的反射reflect
前面我们在学习到struct结构体的时候,因为结构体中的字段首字母大写,而我们想把json文件映射到该结构体上时,需要在在结构体字段后面加上json标签,表明结构体字段和json字段的映射关系.这其中 ...
- Golang的反射reflect深入理解和示例
编程语言中反射的概念 在计算机科学领域,反射是指一类应用,它们能够自描述和自控制.也就是说,这类应用通过采用某种机制来实现对自己行为的描述(self-representation)和监测(examin ...
- golang中的反射reflect详解
先重复一遍反射三定律: 1.反射可以将"接口类型变量"转换为"反射类型对象". 2.反射可以将"反射类型对象"转换为"接口类型变量 ...
- golang反射reflect机制用法
package main import ( "fmt" "reflect" ) type User struct { Id int Name string Ag ...
- Java框架基础——反射(reflect)
一.Class类的使用 1)在面向对象(oop)的世界里,万事万物皆对象. 在Java中,包括基本的数据类型,都是对象. Class c = int.class;//int 的类类型 那就是说: 类是 ...
随机推荐
- 让浏览器重新下载css文件,解决不刷新缓存的问题
网站页面源代码中的css文件和js文件后面带一个问号,后面跟着一连串数字或字符,问号起不到实际作用,仅能当作后缀,如果用问号加参数的方法,可以添加版本号等信息 它的作用有:1.作为版本号,让自己方便记 ...
- Ubuntu 12.04 安装Tomcat7
1.下载Tomcat7 打开Tomcat官网 http://tomcat.apache.org,在左边的导航栏的“Download"中找到Tomcat7.0目录,点击后进入Tomcat7的页 ...
- 【LOJ】#2888. 「APIO2015」巴邻旁之桥 Palembang Bridges
题解 发现我们选择一座桥会选择力\(\frac{s + t}{2}\)较近的一座桥 然后我们只需要按照\(s + t\)排序,然后枚举断点,左边取所有s和t的中位数,右边同理 动态求中位数用平衡树维护 ...
- 关于 facebook
2017/10/29 Facebook账号分分钟被禁用,见怪不怪就好了,禁了就申诉呗 Facebook 如果遇到帐号被停用 / 帐号被封锁,大致上来说有叁个原因: 1, 名字用假名 2, 一个人拥有多 ...
- ubuntu下安装python3及常用爬虫库命令
爬虫常用库安装:
- CSS选择器优先级(转)
原文:http://www.cnblogs.com/wangfupeng1988/p/4285251.html 另外,w3c有文章介绍了CSS选择器的特定性,见https://www.w3.org/T ...
- CSUOJ Water Drinking
Description The Happy Desert is full of sands. There is only a kind of animal called camel living on ...
- 社会地位即服务, Status as a Service (一): 社交网络是一种 ICO 行为?
上周,看到 Eugene Wei 又发了一篇长文,Status as a Service (StaaS).状态即服务?服务器的状态吗?不知所言.抱着好奇,我打开了这篇文章,一看就是 3 个小时!
- Nuxt.js 学习笔记
起源 最主要的原因时使用vue-cli搭建的SPA(单页应用)不利于搜索引擎的SEO操作.搜索引擎对SPA的抓取并不好,特别是百度根本没法抓取到SPA的内容页面,所以我们必须把我们的应用在服务端渲染成 ...
- 循序渐进学.Net Core Web Api开发系列【8】:访问数据库(基本功能)
系列目录 循序渐进学.Net Core Web Api开发系列目录 本系列涉及到的源码下载地址:https://github.com/seabluescn/Blog_WebApi 一.概述 本篇讨论如 ...