Go进阶:反射3定律
各位学习Go语言的朋友,周末好,这次跟大家聊一聊Go语言的一个高级话题:反射。
这篇文章是从我过去的学习笔记修改来的,内容主要来自Go Blog的一篇文章《The law of reflection》。
这篇文章主要介绍反射和接口的关系,解释内在的关系和原理。
反射来自元编程,指通过类型检查变量本身数据结构的方式,只有部分编程语言支持反射。
类型
反射构建在类型系统之上,Go是静态类型语言,每一个变量都有静态类型,在编译时就确定下来了。
比如:
type MyInt intvar i int
var j MyInt
i和j的底层类型都是int
,但i的静态类型是int
,j的静态类型是MyInt
,这两个是不同类型,是不能直接赋值的,需要类型强制转换。
接口类型比较特殊,接口类型的变量被多种对象类型赋值,看起来像动态语言的特性,但变量类型始终是接口类型,Go是静态的。举例:
var r io.Reader
r = os.Stdin
r = bufio.NewReader(r)
r = new(bytes.Buffer)
// and so on
虽然r被3种类型的变量赋值,但r的类型始终是io.Reader
。
最特别:空接口
interface{}
的变量可以被任何类型的值赋值,但类型一直都是interface{}
。
接口的表示
Russ Cox(Go语言创始人)在他的博客详细介绍了Go语言接口,结论是:
接口类型的变量存储的是一对数据:
- 变量实际的值
- 变量的静态类型
例子:
var r io.Reader
tty, err := os.OpenFile("/dev/tty", os.O_RDWR, 0)
if err != nil {return nil, err
}
r = tty
r是接口类型变量,保存了值tty和tty的类型*os.File
,所以才能使用类型断言判断r保存的值的静态类型:
var w io.Writer
w = r.(io.Writer)
虽然r中包含了tty和它的类型,包含了tty的所有函数,但r是接口类型,决定了r只能调用接口io.Reader
中包含的函数。
记住:接口变量保存的不是接口类型的值,还是英语说起来更方便:Interfaces do not hold interface values.
反射的3条定律
定律1:从接口值到反射对象
反射是一种检测存储在接口变量中值和类型的机制。通过reflect
包的一些函数,可以把接口转换为反射定义的对象。
掌握reflect
包的以下函数:
reflect.ValueOf({}interface) reflect.Value
:获取某个变量的值,但值是通过reflect.Value
对象描述的。reflect.TypeOf({}interface) reflect.Type
:获取某个变量的静态类型,但值是通过reflect.Type
对象描述的,是可以直接使用Println
打印的。reflect.Value.Kind() Kind
:获取变量值的底层类型(类别),注意不是类型,是Int、Float,还是Struct,还是Slice,具体见此。reflect.Value.Type() reflect.Type
:获取变量值的类型,效果等同于reflect.TypeOf
。
再解释下Kind和Type的区别,比如:
type MyInt int
var x MyInt = 7
v := reflect.ValueOf(x)
v.Kind()得到的是Int,而Type得到是MyInt
。
定律2:从反射对象到接口值
定律2是定律1的逆向过程,上面我们学了:普通变量 -> 接口变量 -> 反射对象
的过程,这是从反射对象 -> 接口变量
的过程,使用的是Value
的Interface
函数,是把实际的值赋值给空接口变量,它的声明如下:
func (v Value) Interface() (i interface{})
回忆一下:接口变量存储了实际的值和值的类型,Println
可以根据接口变量实际存储的类型自动识别其值并打印。
注意事项:如果Value是结构体的非导出字段,调用该函数会导致panic。
定律3:当反射对象所存的值是可设置时,反射对象才可修改
从定律1入手理解,定律3就不再那么难懂。
Settability is a property of a reflection Value, and not all reflection Values have it.
可设置指的是,可以通过Value设置原始变量的值。
通过函数的例子思考一下可设置:
func f(x int)
在调用f的时候,传入了参数x,从函数内部修改x的值,外部的变量的值并不会发生改变,因为这种是传值,是拷贝的传递方式。
func f(p *int)
函数f的入参是指针类型,在函数内部的修改变量的值,函数外部变量的值也会跟着变化。
使用反射也是这个原理,如果创建Value时传递的是变量,则Value是不可设置的。如果创建Value时传递的是变量地址,则Value是可设置的。
可以使用Value.CanSet()
检测是否可以通过此Value修改原始变量的值。
x := 10
v1 := reflect.ValueOf(x)
fmt.Println("setable:", v1.CanSet())
p := reflect.ValueOf(&x)
fmt.Println("setable:", p.CanSet())
v2 := p.Elem()
fmt.Println("setable:", v2.CanSet())
如何通过Value设置原始对象值呢?
Value.SetXXX()
系列函数可设置Value中原始对象的值。
系列函数有:
- Value.SetInt()
- Value.SetUint()
- Value.SetBool()
- Value.SetBytes()
- Value.SetFloat()
- Value.SetString()
- ...
设置函数这么多,到底该选用哪个Set函数?
根据Value.Kind()
的结果去获得变量的底层类型,然后选用该类别的Set函数。
参考资料
- https://blog.golang.org/laws-of-reflection
- 如果这篇文章对你有帮助,请点个赞/喜欢,感谢。
- 本文作者:大彬
- 如果喜欢本文,随意转载,但请保留此原文链接:http://lessisbetter.site/2019/02/24/go-law-of-reflect/
Go进阶:反射3定律相关推荐
- go interface转int_24. 图解 Go语言中的反射三定律
大家好,我是明哥. 由于某些不可抗力,之前该专栏的所有内容都被删除了.因此之后的一段时间内,我会重新发布这些文章,先给已经看过的关注者提个醒. 本文原文:http://golang.iswbm.com ...
- Go语言的反射三定律
这里先抛出GO语言的反射三定律,然后一一进行讲解: 1.反射可以将"接口类型变量"转换为"反射类型对象" 2.反射可以将"反射类型对象"转换 ...
- java 反射 hold_Go进阶:反射3定律
各位学习Go语言的朋友,周末好,这次跟大家聊一聊Go语言的一个高级话题:反射. 这篇文章是从我过去的学习笔记修改来的,内容主要来自Go Blog的一篇文章<The law of reflecti ...
- 面向对象进阶-反射(二)重要知识点
# 面向对象的进阶# 其他常用模块# 作业 考试题# 网络编程 2天# ftp作业 # class A:pass# class B(A):pass# a = A()# print(isinstance ...
- python_day21面向对象的进阶(反射,内置方法,)
# 两个内置函数 *# 反射 *****# 内置方法 *** # 类(定义) # 静态属性 类属性(变量) 直接写在类中,全大写 # 动态属性 方法(函数) self # 类方法 @classmeth ...
- 0418 面向对象进阶 反射内置函数
绑定:class A:def func1(self):pass # 对象与方法之间的绑定def func4(self): pass # 对象与方法之间的绑定 @classmethoddef func2 ...
- 12_Go语言 反射详解
1. 反射定义: 可以在运行时动态获取变量的相关信息. Import ("reflect") 官方对此有个非常简明的介绍,两句话耐人寻味: 反射提供一种让程序检查自身结构的能力.再 ...
- Golang的反射reflect深入理解和示例
[TOC] Golang的反射reflect深入理解和示例 [记录于2018年2月] 编程语言中反射的概念 在计算机科学领域,反射是指一类应用,它们能够自描述和自控制.也就是说,这类应用通过采用某种机 ...
- 深度解密Go语言之反射
反射和 Interface 息息相关,而 Interface 是我们上一篇文章的内容.在开始正文前,和大家说点题外话. 上一篇关于 Interface 的文章发出后,获得了很多的关注和阅读.比如,登上 ...
最新文章
- 刨根问底: Kafka 到底会不会丢数据?
- 2015年3月-前端开发月刊
- Counterfeit Dollar
- facebook 文本分类_如何禁用和自定义Facebook的通知,文本和电子邮件
- 关于.NET5在IIS中部署的几个问题总结
- python tornado websocket_Python Tornado实现WEB服务器Socket服务器共存并实现交互的方法...
- 此变量非彼变量(python变量)
- vivado和modelsim联合仿真实现奇分频
- kali 切换图形界面_kali切换桌面环境
- 全网最新正则表达式总结- 简单 便捷 (适用于新手学习正则表达式,老手巩固学习正则表达式)
- Oracle的逻辑读和物理读
- 【0】依照Django官网:实现第一个django app 安装必要的工具/模块
- 黄冈师范学院计算机科学与技术学院,黄冈师范学院 计算机科学与技术学院 刘小俊老师简介 联系方式 手机电话 邮箱...
- 【Vue3】vue3的keepAlive保存滚动位置
- luminati 还原IP端口数据
- 电路邱关源学习笔记——1.7基尔霍夫定律
- python 百度智能完善拆分识别收货人地址
- style-loader 与css-loader 处理 css样式文件
- python翻译成中文蟒蛇_python为什么叫大蟒蛇
- 生成对抗网络(Generative Adversarial Networks,GAN)初探
热门文章
- php fsockopen解决办法
- ASP.NET虚拟主机的重大安全隐患
- 如何获取主机的IP址址
- ASP.NET结合COM组件发送Email
- 程序员的自我修养--链接、装载与库笔记:Linux共享库的组织
- Ubuntu14.04 64位机上安装cuda8.0+cudnn5.0操作步骤
- 有效的rtsp流媒体测试地址汇总
- FFmpeg简介及在vc2010下编译步骤
- VC++ OCX 控件注册
- 【FFmpeg】截至ffmpeg4.2不推荐(Deprecate)继续使用的接口,以及代替它的接口汇总