golang 反射

 

参考:|--http://blog.51cto.com/speakingbaicai/1707637

   |--https://studygolang.com/articles/6324

  反射是在golang程序运行时检查变量所具有类型的一种机制。由于反射可以得出关于变量结构数据(即“关于数据的数据”),所以这也被认为是golang元编程的基础。我们由反射三法则入手:

从类型和方法理解反射内涵

  在基本的层面上,反射只是一个检查存储在接口变量中的类型和值的算法。使用反射机制,首先需要导入reflect包,reflect包中有两个重要类型需要了解,reflect.Type和reflect.Value,这两个类型使得可以访问变量的内容。与此相关的,还有两个简单的函数,reflect.TypeOf和reflect.ValueOf,可以从接口值中分别获取reflect.Type和reflect.Value。

     初学可能会认为reflect.Type和reflect.Value是一种并列关系,但其实它们是一种包含关系,我们结合一段代码来理解这段话。

import ("fmt""reflect"
)func main() {var x float64 = 1.1fmt.Println("reflect.Value:", reflect.ValueOf(x))fmt.Println("reflect.Type:", reflect.TypeOf(x))v := reflect.ValueOf(x)fmt.Println("reflect.Type:",v.Type())fmt.Println("actual value:", v.Float())fmt.Println("kind is float64?", v.Kind() == reflect.Float64)
}

根据程序及其结果,我们可以发现:在go语言中,每个值都包含两个内容:类型和实际的值。从类型角度来看,reflect.Value是一个关于<类型, 实际的值>的二元组,而reflect.Type是值的类型,二者是包含关系。从方法角度来看,reflect.TypeOf 和 (reflect.ValueOf(x)).Type都可以返回reflect.Type;(reflect.ValueOf(x)).Float可以返回实际的值(类似的方法还包括(reflect.ValueOf(x)).Int、(reflect.ValueOf(x)).Bool等);(reflect.ValueOf(x)).Kind可以返回一个常量定义的类型。

    根据上述分析,我们可以得出一个示意图,更为直观形象的表明值、类型、实际的值的关系。

此外,golang采用静态类型机制,TypeOf返回静态类型;但是,Kind返回底层类型。我们同样以一段代码来验证这段话。

import ("fmt""reflect"
)type MyInt intfunc main() {var x MyInt = 1v := reflect.ValueOf(x)fmt.Println("reflect.Type:", v.Type())fmt.Println("kind is int?", v.Kind() == reflect.Int)
}

反射三法则:

1 法则一:从接口值到反射对象的反射(Reflection goes from interface value toreflection object)

    前文所述内容其实就是从接口值到反射对象的反射,代表方法为reflect.ValueOf和reflect.TypeOf。可能有人会问,接口?接口在哪呢?我们来看一些前文提到这两个函数的声明,函数的参数是空接口,其实接口就在那里。

func ValueOf(i interface{}) Value
func TypeOf(i interface{}) Type

2 法则二:从反射对象到接口值的反射(Reflection goes from reflection object to interface value)

    从reflect.Value可以使用Interface方法还原接口值;此方法可以高效地打包类型和值信息到接口表达中,并返回这个结果。方法声明:

func (v Value) Interface() interface{}

    通过反射对象 v 可以打印 float64 的表达值。

y :=v.Interface().(float64) // y 将为类型 float64。
fmt.Println(y)

    还有更为简洁的实现。fmt.Println,fmt.Printf等其他所有传递一个空接口值作为参数的函数,在 fmt包内部解包的方式就像之前的例子这样。因此正确的打印reflect.Value的内容的方法就是将Interface方法的结果进行格式化打印(formatted print routine). 

fmt.Println(v.Interface())

    为什么不是fmt.Println(v)?因为v是一个 reflect.Value;这里希望获得的是它保存的实际的值。

    我们修改前文代码还进行验证:

func main() {var x float64 = 1.1fmt.Println("reflect.Value:", reflect.ValueOf(x))fmt.Println("reflect.Type:", reflect.TypeOf(x))v := (reflect.ValueOf(x))fmt.Println("reflect.Type:", v.Type())fmt.Println("actual value(interface):", v.Interface())fmt.Println("kind is float64?", v.Kind() == reflect.Float64)
}

其输出:

进一步地,我们可以修改上述关系示意图,新图更为简洁优雅:

3. 为了修改反射对象,其值必须可设置(To modify a reflectionobject, the value must be settable)

    反射对象可以通过SetFloat等方法设置值,通过CanSet判断可设置性。但是这里面有坑,有些值是不可设置的。我们还是通过一段代码来看。

import ("fmt""reflect"
)func main() {var x float64 = 1.1v := reflect.ValueOf(x)fmt.Println("settability of v:",v.CanSet())v.SetFloat(1.2)
}

 

其结果表明,反射对象v是不可设置的,如果硬要设置的话,会有panic异常。

    为什么不能设置呢?我们可以从函数传参的角度来思考这个问题。V := reflect.ValueOf(x),这个函数是值传递,即传递了一个x的副本到函数中,而非x本身。我们都知道,值传递的参数是不能被真正修改的。

    我最初还有过这样的想法:v和x又不是一个变量,x不能被修改,但是v应该可以被修改啊。完全从形式上考虑,这样似乎有道理。但是再多想一层,如果允许执行,虽然v可以被修改,但是却无法更新x。也就是说,在反射值内部允许修改x的副本,但是x本身却不会受到这个影响。这会造成混乱,并且毫无意义,因此在golang中这样操作是非法的。

    让我们重新用函数传参的角度思考这个问题。如果传递副本不能修改,那我们就通过就传递指针好了。我们来试试:

func main() {var x float64 = 1.1p := reflect.ValueOf(&x)fmt.Println("type of p:",p.Type())fmt.Println("settability of p:",p.CanSet())
}

  

  还是不行。因为p的实际类型是*float64,而非float64,这样修改相当于要直接修改地址了。

  我们可以借助Elem方法,通过指针来修改指针指向的具体值。

func (v Value)Elem() Value
func main() {var x float64 = 1.1p := reflect.ValueOf(&x)fmt.Println("type of p:",p.Type())v := p.Elem()fmt.Println("type of v:",v.Type())fmt.Println("settability of v:",v.CanSet())
}

 

这样就可以进行修改了。虽然p是不可修改的,但是v可以修改。这种方法思路上类似引用传参,传入地址,修改地址所指向的具体值。

 

 

转载于:https://www.cnblogs.com/K-artorias/p/8962901.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/427780.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

java硬件编程_关于JAVA并发编程你需要知道的——硬件篇

无论程序语言如何千变万化&#xff0c;他们都深深地根植于目前的计算机体系结构。左图是intel CPU的三级高速缓存设计&#xff0c;由于高速缓存对程序员基本不可见&#xff0c;因此可以抽象为右图。缓存的设计首先还是先谈谈左图。L1-cache分为两部分&#xff0c;i-cache存储指…

net Core做一个webApi的简单实例

用NetCore 和Dapper 和mySql做一个简单的实例&#xff0c; 一准备工作 1&#xff1a;VS2017windos系统&#xff0c;也可以用其他的操作系统和工具 2&#xff1a;一台Cenetos的虚拟机或者虚拟机 二&#xff1a;开始 1&#xff1a;用微软官方的netCore的ToDo项目改造&#xff0c;…

java 文件输出流_Java 文件输出流

Java IO教程 - Java文件输出流创建输出流要写入文件&#xff0c;我们需要创建一个FileOutputStream类的对象&#xff0c;它将表示输出流。// Create a file output streamString destFile "test.txt";FileOutputStream fos new FileOutputStream(destFile);当写入文…

MySQL5.7参数log_timestamps

最近测试MySQL 5.7.21 Community Server这个版本的MySQL数据库时&#xff0c;发现其错误日志的时间跟系统当前时间不一致&#xff0c;后面检查发现日期时间格式都是UTC时间&#xff0c;查了一下相关资料&#xff0c;原来在MySQL 5.7.2 之后日志文件里面的时间戳从默认的本地系…

Tidb集群加mysql_TiDB - 快速入门,集群搭建

TiDB 是开源分布式关系型数据库&#xff0c;是一款同时支持在线事务处理与在线分析处理 (Hybrid Transactional and Analytical Processing, HTAP)的融合型分布式数据库产品&#xff0c;具备水平扩容或者缩容、金融级高可用、实时 HTAP、云原生的分布式数据库、兼容 MySQL 5.7 …

python递归函数

递归函数简单来说就是函数的自我调用。使用递归函数很多时候可以使得代码简洁&#xff0c;优雅。可以把复杂的问题分解成简单的子问题。递归有无与伦比的魅力&#xff0c;从著名的计算机名言就可以看出递归的奇妙&#xff1a; To iterate is human,to recurse divine. 迭代者为…

java知识体系 servlet_03-Servlet 体系结构知识梳理

一、Servlet体系结构Java Web应用是基于Servlet规范运行&#xff0c;Servlet顶层类的关联如下图&#xff1a;从图可看出&#xff0c;Servlet规范基本围绕这几个类运行&#xff0c;其中&#xff0c;与Servlet主动关联的有3个类&#xff0c;分别是ServletRequest、ServletRespons…

testlink自带java api_java如何连接testlink

1.下载相关的jar包2.获取到testlink的url和key&#xff0c;注意&#xff1a;url不是testlink的连接地址&#xff0c;是连接地址/lib/api/xmlrpc.php3.测试是否连接成功public static void main(String args[]) {String url "http://test.tl.gmsd.lan/lib/api/xmlrpc.php&…

lr背景虚化_lr背景虚化_怎样拍出背景模糊的照片

除了锐化之外&#xff0c;要获得独特的&#xff0c;令人难忘的图像&#xff0c;还可以使用其他方法&#xff0c;例如&#xff0c;相反的效果-单个细节的模糊。这样的方法将使人们有可能专注于整个构图的中心人物&#xff0c;为图片增加情感色彩&#xff0c;动作&#xff0c;并为…

在线五子棋JAVA网络编程_实验五 Java网络编程及安全

一、实验内容1&#xff0e;掌握Socket程序的编写&#xff1b;2&#xff0e;掌握密码技术的使用&#xff1b;3&#xff0e;设计安全传输系统。二、实验步骤1. 基于Java Socket实现安全传输2. 基于TCP实现客户端和服务器&#xff0c;结对编程一人负责客户端&#xff0c;一人负责服…

rnn中文语音识别java_语音识别算法阅读之RNN-T-2018

论文&#xff1a;EXPLORING ARCHITECTURES, DATA AND UNITS FOR STREAMING END-TO-END SPEECH RECOGNITION WITH RNN-TRANSDUCER,2018CTC的一个问题在于&#xff0c;其假设当前帧的输出与历史输出之间的条件独立性&#xff1b;RNN-T引入预测网络来弥补CTC这种条件独立性假设带来…

Storm环境搭建(分布式集群)

作为流计算的开篇&#xff0c;笔者首先给出storm的安装和部署&#xff0c;storm的第二篇&#xff0c;笔者将详细的介绍storm的工作原理。下边直接上干货&#xff0c;跟笔者的步伐一块儿安装storm。 原文链接&#xff1a;Storm环境搭建&#xff08;分布式集群&#xff09; Step1…

18.QT-QPlainEdit 信号与槽

QPlainEdit编辑功能 Public Slots void appendHtml ( const QString & html ) void appendPlainText ( const QString & text ) void centerCursor () void clear () void copy () void cut () void insertPlainText ( const QString & text ) void paste () void …

嘻嘻

今天我们来聊一下如何减肥&#xff1f; 其实我也不知道&#xff0c;嘻嘻~ 开个玩笑 好了&#xff0c;今天我们继续来学习新的知识。 在前两篇文章中&#xff0c;我们接触到了索引的概念&#xff0c;今天我们要对索引进行一个拓展。首先我们先来看一下下面这段代码&#xff1a; …

mysql :完整性约束

---恢复内容开始--- 一&#xff1a;介绍 约束条件与数据类型的宽度一样 &#xff0c;都是可选参数 作用&#xff0c;用于保证数据的完整性和 一致性 主要分为&#xff1a; primary key (pk) 标识该字段为该表的主键&#xff0c; 可以唯一的标识记录 foreign key &#xff08;fk…

php htts cookies,Http和Https下的cookie的写入问题

session和cookie是不一样的session存储在服务器,cookie存储在客户端设置cookie:function setcookie ($name, $value null, $expire null, $path null, $domain null, $secure null, $httponly null) {}获取cookie:$_COOKIE[$sCookieName];题主的写法只是操作了服务器端的…

java 做项目踩坑,web项目踩坑过程

sql函数设计&#xff1a;一开始本来是直接用Java的jdbc直接传输操作语句的。但后来学了存储过程发现存储过程可以提高不少的效率。就重构了自己对数据库的操作代码。包括&#xff1a;开启&#xff0c;查找&#xff0c;修改&#xff0c;关闭。开启&#xff1a;直接使用的构造函数…

matlab设计理想数字带通滤波器,基于matlab的数字带通滤波器课程设计报告

基于matlab的数字带通滤波器课程设计报告 1 西安文理学院机械电子工程系 课程设计报告 专业班级 08级电子信息工程1班 题 目 基于 MATLAB 的数字带通滤波器 学 号 学生姓名 指导教师 2011 年 12 月 西安文理学院机械电子工程系2 课程设计任务书 学生姓名 _______专业班级 _____…

xml序列号错误

xml序列号错误((XmlHelper.Deserialize))提示&#xff1a;XML 文档(1, 2)中有错误。{"不应有 <entryOrder xmlns>。"} 原因&#xff1a;1.缺少根目录&#xff08;<root>&#xff09;2.xml字段转换失败&#xff08;string->int&#xff09; ----------…

关于windows10 CMD 的一些操作

之前接触过cmd的一些操作方法&#xff0c;比如用dir、tasklist等一些方法&#xff0c;但是用了会立马忘记&#xff0c;再用到时又要重新google&#xff0c;这着实让我头痛&#xff01;&#xff01;&#xff01; 今天又碰到一个关于改变目录的问题&#xff0c;又是纠结万分&…