各位学习Go语言的朋友,周末好,这次跟大家聊一聊Go语言的一个高级话题:反射。

这篇文章是从我过去的学习笔记修改来的,内容主要来自Go Blog的一篇文章《The law of reflection》。

这篇文章主要介绍反射和接口的关系,解释内在的关系和原理。

反射来自元编程,指通过类型检查变量本身数据结构的方式,只有部分编程语言支持反射。

类型

反射构建在类型系统之上,Go是静态类型语言,每一个变量都有静态类型,在编译时就确定下来了。

比如:

type MyInt int

var 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函数。

参考资料

java 反射 hold_Go进阶:反射3定律相关推荐

  1. java 反射 int_Java 反射由浅入深 | 进阶必备

    原标题:Java 反射由浅入深 | 进阶必备 一.Java 反射机制 参考了许多博文,总结了以下个人观点,若有不妥还望指正: Java 反射机制在程序运行时,对于任意一个类,都能够知道这个类的所有属性 ...

  2. java 反射代价_Java反射机制

    # 反射 ## 什么是反射 反射是Java提供的动态执行机制, 可以动态加载类, 动态创建对象, 动态访问属性, 动态调用方法.. 静态执行: Java代码经过编译以后就确定的执行次序, 称为静态执行 ...

  3. java 反射机制_Java反射机制原理探究

    反射是Java中的一个重要的特性,使用反射可以在运行时动态生成对象.获取对象属性以及调用对象方法.与编译期的静态行为相对,所有的静态型操作都在编译期完成,而反射的所有行为基本都是在运行时进行的,这是一 ...

  4. Java中为什么需要反射?反射要解决什么问题?

    Java中为什么需要反射?反射要解决什么问题? 参考文章: (1)Java中为什么需要反射?反射要解决什么问题? (2)https://www.cnblogs.com/buoge/p/9285142. ...

  5. Java基础-注解和反射

    Java基础-注解和反射 前言 对于注解,我主要还是在自定义APT还有运行时反射获取类来让自己能够构建出复用性更高的代码逻辑. 知识点1-注解: 注解的应用场景由元注解@Retention来进行指定, ...

  6. Java基础教程:反射基础

    Java基础教程:反射基础 引入反射 反射是什么 能够动态分析类能力的程序称为反射. 反射是一种很强大且复杂的机制. Class类 在程序运行期间,Java运行时系统始终为所有对象维护一个被称为运行时 ...

  7. java泛型怎么用反射生成_Java 之 使用反射生成并操作对象

    一.使用反射创建对象 通过反射来生成对象有如下两种方式: 方式一: 使用 Class 对象的 newInstance() 方法来创建 Class 对象对应类的实例,这种方法要求该 Class 对象的对 ...

  8. Java 反射机制[Field反射]

    Java 反射机制[Field反射] 1.  反射概念及功能 反射就是把Java类中的各种成分映射成对应的Java类.比如一个Java类中用一个Class类的对象来表示. 一个类中的组成部分分为成员变 ...

  9. java反射模式_Java反射机制详解

    对于一般的开发者,很少需要直接使用Java反射机制来完成功能开发,但是反射是很多框架譬如 Spring, Mybatis 实现的核心,反射虽小,能量却很大. 本文主要介绍反射相关的概念以及API的使用 ...

最新文章

  1. statpot:使用mongo+bootstrap+highcharts做统计报表
  2. 为反对种族歧视,GitHub正在把master/slave等术语替换掉
  3. 基于Pyhton的图像隐写术--如何隐藏图像中的数据
  4. 基础才是重中之重~何为原子化操作
  5. mysql 查询任务_mysql中怎样显示服务器正在执行的sql任务
  6. 学python爬虫需要什么基础-从零开始教你学爬虫!python爬虫的基本流程!
  7. nand flash坏块管理OOB,BBT,ECC
  8. 使用Spring Data R2DBC进行异步RDBMS访问
  9. macosx jdk_MacOSX环境上的多个Java JDK
  10. 用chrome模拟微信浏览器访问需要OAuth2.0网页授权的页面
  11. $limit、$skip、$unwind
  12. 19c 新特性: Hint Usage Reports详解
  13. java程序设计与问题求解第八版pdf_《Java程序设计与问题求解(第7版)》怎么样_目录_pdf在线阅读 - 课课家教育...
  14. tomcat启动报错解决org.jaxen.util.AncestorOrSelfAxisIterator
  15. html简介百度百科,outerHTML
  16. 显示前半内容后半内容用省略号_220六语文课文内容填空与句子练习
  17. 利用智能算法优化参数的自适应变分模态分解,VMD实现混合储能系统的分频
  18. SwiftUI教程第1章第20:加载Web图片
  19. 用python让excel飞起来(第5章 单元格操作)
  20. nginx服务,杀不死nginx进程

热门文章

  1. java经典面试题目
  2. 小汤学编程之JavaEE学习day02——Servlet使用步骤、生命周期、接收与返回数据、GET与POST、转发与重定向
  3. 小汤学编程之JAVA基础day04——流程结构
  4. 【PyTorch v1.1.0文档研习】60分钟快速上手
  5. gulp与webpack的对比
  6. Codeforces 1091E New Year and the Acquaintance Estimation Erdős–Gallai定理
  7. ●BZOJ 1396 识别子串
  8. 2017/08/22 工作日志
  9. Android Service完全解析
  10. 在Windows2003 SP2上安装VS2005SP1遇到的问题