Golang之路---03 面向对象——反射

反射

反射的存在意义

在开发中,你或许会碰到在有些情况下,你需要获取一个对象的类型,属性及方法,而这个过程其实就是反射。
golang中变量包括(type, value)两部分

  1. 静态类型
    所谓的静态类型(即 static type),就是变量声明的时候的类型。
var age int   // int 是静态类型
var name string  // string 也是静态类型

它是你在编码时,肉眼可见的类型。

  1. 动态类型
    所谓的动态类型(即 concrete type,也叫具体类型)是程序运行时系统才能看见的类型。
var i interface{}
i = 18
i = "Golang"

  第一行:我们在给 i 声明了 interface{} 类型,所以 i 的静态类型就是 interface{}
  第二行:当我们给变量 i 赋一个 int 类型的值时,它的静态类型还是 interface{},这是不会变的,但是它的动态类型此时变成了 int 类型。
  第三行:当我们给变量 i 赋一个 string 类型的值时,它的静态类型还是 interface{},它还是不会变,但是它的动态类型此时又变成了 string 类型。
  从以上,可以知道,不管是 i=18 ,还是 i=“Golang”,都是当程序运行到这里时,变量的类型,才发生了改变,这就是我们最开始所说的动态类型是程序运行时系统才能看见的类型。

  1. 接口组成
      每个接口变量,实际上都是由一 pair 对(type 和 data)组合而成,pair 对中记录着实际变量的值和类型。
    比如下面这条语句
var age int = 25

我们声明了一个 int 类型变量,变量名叫 age ,其值为 25。

  1. 接口细分
    一个interface{}类型的变量包含了2个指针,一个指针指向值的类型【对应concrete type】,另外一个指针指向实际的值【对应value】。
    根据接口是否包含方法,可以将接口分为 iface 和 eface。
    iface
    第一种:iface,表示带有一组方法的接口。
    比如
type Phone interface {call()
}

在这里插入图片描述
  eface
  第二种:eface,表示不带有方法的接口

  比如

var i interface{}

在这里插入图片描述
先来看看 iface,有如下一段代码:

var reader io.Readertty, err := os.OpenFile("/dev/tty", os.O_RDWR, 0)
if err != nil {return nil, err
}reader = tty

第一行代码:var reader io.Reader ,由于 io.Reader 接口包含 Read 方法,所以 io.Reader 是 iface,此时 reader 对象的静态类型是 io.Reader,暂无动态类型。
在这里插入图片描述
最后一行代码:reader = tty,tty 是一个 *os.File 类型的实例,此时reader 对象的静态类型还是 io.Reader,而动态类型变成了 *os.File。
在这里插入图片描述
再来看看 eface,有如下一段代码:

//不带函数的interface
var empty interface{}tty, err := os.OpenFile("/dev/tty", os.O_RDWR, 0)
if err != nil {return nil, err
}empty = tty

第一行代码:var empty interface{},由于 interface{} 是一个 eface,其只有一个 _type 可以存放变量类型,此时 empty 对象的(静态)类型是 nil。
在这里插入图片描述
最后一行代码:empty = tty,tty 是一个 *os.File 类型的实例,此时 _type 变成了 *os.File。
在这里插入图片描述
由于动态类型的存在,在一个函数中接收的参数的类型有可能无法预先知晓,此时我们就要对参数进行反射,然后根据不同的类型做不同的处理。反射就是用来检测存储在接口变量内部(值value;类型concrete type) pair对的一种机制。

反射中的Type 和 Value

在这里插入图片描述
在真实世界里,type 和 value 是合并在一起组成接口变量(pair对)的。
而在反射的世界里,type 和 data 却是分开的,他们分别由 reflect.Type 和 reflect.Value 来表现。

Golang里有个反射三定律

  1. Reflection goes from interface value to reflection object.

  2. Reflection goes from reflection object to interface value.

  3. To modify a reflection object, the value must be settable.

翻译一下,就是:

反射可以将接口类型变量 转换为“反射类型对象”;

反射可以将 “反射类型对象”转换为 接口类型变量;

如果要修改 “反射类型对象” 其类型必须是 可写的;

第一定律

  为了实现从接口变量到反射对象的转换,需要提到 reflect 包里很重要的两个方法:
  reflect.TypeOf(i) :获得接口值的类型
  reflect.ValueOf(i):获得接口值的值
  这两个方法返回的对象,我们称之为反射对象:Type object 和 Value object。

import ("fmt""reflect"
)func main() {var age interface{} = 25//%T:输出值的类型  %v: 使用默认格式输出值fmt.Printf("原始接口变量的类型为 %T,值为 %v\n",age,age)t := reflect.TypeOf(age)v := reflect.ValueOf(age)//从接口变量到反射对象fmt.Printf("从接口变量到反射对象:type对象的类型为 %T\n",t)fmt.Printf("从接口变量到反射对象:value对象的类型为 %T\n",v)
}

在这里插入图片描述

第二定律

  完成从反射对象到接口变量的转换
注意:只有 Value 才能逆向转换,而 Type 则不行

func main() {var age interface{} = 25//%T:输出值的类型  %v: 使用默认格式输出值fmt.Printf("原始接口变量的类型为 %T,值为 %v\n",age,age)t := reflect.TypeOf(age)v := reflect.ValueOf(age)//从接口变量到反射对象fmt.Printf("从接口变量到反射对象:type对象的类型为 %T\n",t)fmt.Printf("从接口变量到反射对象:value对象的类型为 %T\n",v)//从反射对象到接口变量//通过Interface函数实现i := v.Interface()fmt.Printf("从接口变量到反射对象:value对象的类型为 %T 值为%v\n",i,i)
}

在这里插入图片描述
最后转换后的对象,静态类型为 interface{} ,如果要转成最初的原始类型,需要再类型断言转换一下

第三定律

  反射世界是真实世界的一个『映射』,是我的一个描述,但这并不严格,因为并不是你在反射世界里所做的事情都会还原到真实世界里。

  第三定律引出了一个 settable (可设置性,或可写性)的概念。

  Golang 语言里的函数都是值传递,只要你传递的不是变量的指针,你在函数内部对变量的修改是不会影响到原始的变量的。

  回到反射上来,当你使用 reflect.Typeof 和 reflect.Valueof 的时候,如果传递的不是接口变量的指针,反射世界里的变量值始终将只是真实世界里的一个拷贝,你对该反射对象进行修改,并不能反映到真实世界里。

因此在反射的规则里还要注意

  1. 不是接收变量指针创建的反射对象,是不具备『可写性』的

  2. 是否具备『可写性』,可使用 CanSet() 来获取得知

  3. 对不具备『可写性』的对象进行修改,是没有意义的,也认为是不合法的,因此会报错。

	var name string = "hello world"v := reflect.ValueOf(name)fmt.Println("可写性为:",v.CanSet())

在这里插入图片描述
要让反射对象具备可写性,需要注意两点

  1. 创建反射对象时传入变量的指针

  2. 使用 Elem()函数返回指针指向的数据

func main() {var name string = "hello world"v := reflect.ValueOf(&name)fmt.Println("可写性为:",v.CanSet())v2 := v.Elem()fmt.Println("v2可写性为:",v2.CanSet())
}

在这里插入图片描述

反射中的函数

获取类别:Kind()

  Type 对象 和 Value 对象都可以通过 Kind() 方法返回对应的接口变量的基础类型。

reflect.TypeOf(m).Kind()
reflect.ValueOf(m).Kind()

  在这里,要注意的是,Kind 和 Type 是有区别的,Kind 表示更基础,范围更广的分类。

  有一个例子来表示, iPhone (接口变量)的 Type 是手机,Kind 是电子产品。
   Kind 表示的基本都是 Go 原生的基本类型(共有 25 种的合法类型)

type Kind uintconst (Invalid Kind = iota  // 非法类型Bool                 // 布尔型Int                  // 有符号整型Int8                 // 有符号8位整型Int16                // 有符号16位整型Int32                // 有符号32位整型Int64                // 有符号64位整型Uint                 // 无符号整型Uint8                // 无符号8位整型Uint16               // 无符号16位整型Uint32               // 无符号32位整型Uint64               // 无符号64位整型Uintptr              // 指针Float32              // 单精度浮点数Float64              // 双精度浮点数Complex64            // 64位复数类型Complex128           // 128位复数类型Array                // 数组Chan                 // 通道Func                 // 函数Interface            // 接口Map                  // 映射Ptr                  // 指针Slice                // 切片String               // 字符串Struct               // 结构体UnsafePointer        // 底层指针
)

/* kink函数的用法 */
func main() {m := Profile{}t := reflect.TypeOf(m)/* Type: main.ProfileKind  struct */fmt.Println("Type:",t)//1.传入值fmt.Println("Kind ",t.Kind())//2.传入指针v := reflect.ValueOf(&m)/* &m Type : *main.Profile&m Kind : ptrm Type : main.Profilem Kind : struct */fmt.Println("&m Type :",v.Type())fmt.Println("&m Kind :",v.Kind())//Elem会返回 Type 对象所表示的指针指向的数据fmt.Println("m Type :",v.Elem().Type())fmt.Println("m Kind :",v.Elem().Kind())}

进行类型的转换

eg: Int() :转成 int
示例代码如下

package mainimport ("fmt""reflect"
)func main() {var age int = 25v1 := reflect.ValueOf(age)fmt.Printf("转换前, type: %T, value: %v \n", v1, v1)v2 := v1.Int()fmt.Printf("转换后, type: %T, value: %v \n", v2, v2)
}

在这里插入图片描述

对属性的操作

func main() {p := Pearson{"xy",21,"male"}v:=reflect.ValueOf(p)//NumField()fmt.Println("字段数:",v.NumField())//Fieldfmt.Println("第一个字段:",v.Field(0))fmt.Println("第一个字段:",v.Field(1))fmt.Println("第一个字段:",v.Field(2))//通过遍历的方式进行打印for i:=0;i<v.NumField();i++{fmt.Printf("第 %d个字段:%v\n",i+1,v.Field(i))}
}

在这里插入图片描述

对方法的操作

type Pearson struct{name stringage intgender string
}func(p Pearson)SayBye(){fmt.Println("Bye")
}func(p Pearson)SayHello(){fmt.Println("Hello")
}func main() {p := &Pearson{"xy",21,"male"}
//注意使用 TypeOfv:=reflect.TypeOf(p)fmt.Println("方法数:",v.NumMethod())fmt.Println("第一个方法:",v.Method(0).Name)fmt.Println("第一个方法:",v.Method(1).Name)//通过遍历的方式进行打印for i:=0;i<v.NumMethod();i++{fmt.Printf("第 %d个方法:%v\n",i+1,v.Method(i).Name)}
}

在这里插入图片描述

反射说明

  Golang 的反射很慢,这个和它的 API 设计有关
  不到不得不用的地步,能避免使用反射就不用。

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

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

相关文章

第四次作业 运维高级 安装tomcat8和部署jpress应用

1. 简述静态网页和动态网页的区别。 静态网页 静态网页是指存放在服务器文件系统中实实在在的HTML文件。当用户在浏览器中输入页面的URL&#xff0c;然后回车&#xff0c;浏览器就会将对应的html文件下载、渲染并呈现在窗口中。早期的网站通常都是由静态页面制作的。 静态网页…

【Spring框架】SpringBoot统一功能处理

目录 用户登录权限校验用户登录拦截器排除所有静态资源练习&#xff1a;登录拦截器拦截器实现原理 统一异常处理统一数据返回格式为什么需要统⼀数据返回格式&#xff1f;统⼀数据返回格式的实现 用户登录权限校验 用户登录拦截器 1.自定义拦截器 package com.example.demo.…

shell centos 7 一键部署 KVM软件脚本

这个脚本有限地方还需要完善下 设计思路&#xff1a; 1、创建检查内核函数 check_kernel() 2、创建升级内核函数 update_kernel() 3、创建检查是否支持虚拟化函数 check_virtual() 4、创建检查操作系统函数 check_system() 5、创建检查网络函数 check_network() 6…

MicroPython ESP32网页实时更新DHT11数据显示

MicroPython ESP32网页实时更新DHT11数据显示 &#x1f4cc;相关篇《MicroPython ESP32 读取DHT11温湿度传感器数据》&#x1f4cd;《【Micropython esp32/8266】网页点灯控制示例》 ✨本例综合以上两篇文章内容实现&#xff1a;在本地网页中显示DHT11温度传感器数据。可以做到…

【Clion 2】使用技巧

一、TODO: 说明&#xff1a; 有时需要标记部分代码以供将来参考&#xff1a; 优化和改进的领域、可能的更改、要讨论的问题等等。 支持&#xff1a; TODO和FIXME小写和大写。这些模式可以在任何受支持的文件类型的行注释和块注释内使用。 创建TODO项 在要添加注释的代码行中…

Python web实战之 Django 的 MVC 设计模式详解

技术栈&#xff1a;Python、Django、HTML、CSS、JavaScript。 概要 在 Web 开发中&#xff0c;MVC&#xff08;Model-View-Controller&#xff09;模式是一种非常常见的设计模式&#xff0c;它可以帮助我们更好地管理代码&#xff0c;提高代码的可维护性。今天就介绍如何使用 …

RTT(RT-Thread)线程管理(1.2W字详细讲解)

目录 RTT线程管理 线程管理特点 线程工作机制 线程控制块 线程属性 线程状态之间切换 线程相关操作 创建和删除线程 创建线程 删除线程 动态创建线程实例 启动线程 初始化和脱离线程 初始化线程 脱离线程 静态创建线程实例 线程辅助函数 获得当前线程 让出处…

数组中的第K个最大元 O(N)

给定整数数组 nums 和整数 k&#xff0c;请返回数组中第 k 个最大的元素。 请注意&#xff0c;你需要找的是数组排序后的第 k 个最大的元素&#xff0c;而不是第 k 个不同的元素。 你必须设计并实现时间复杂度为 O(n) 的算法解决此问题。 示例 1: 输入: [3,2,1,5,6,4], k 2…

[腾讯云Cloud Studio实战训练营]无门槛使用GPT+Cloud Studio辅助编程完成Excel自动工资结算

目录 前言一、Cloud Studio产品介绍1.1 注册Cloud Studio 二、项目实验2.1 选择合适的开发环境2.2 实验项目介绍2.3 实验步骤三、总结 前言 chatgpt简单介绍: ChatGPT是一种基于GPT的自然语言处理模型&#xff0c;专门用于生成对话式文本。它是OpenAI于2021年发布的&#xff0…

突破传统监测模式:业务状态监控HM的新思路 | 京东云技术团队

一、传统监控系统的盲区&#xff0c;如何打造业务状态监控。 在系统架构设计中非常重要的一环是要做数据监控和数据最终一致性&#xff0c;关于一致性的补偿&#xff0c;已经由算法部的大佬总结过就不再赘述。这里主要讲如何去补偿&#xff1f;补偿的方案哪些&#xff1f;这就…

TCP/IP协议

TCP/IP 是一类协议系统&#xff0c;它是用于网络通信的一套协议集合 物理层 所谓的物理层&#xff0c;是指光纤、电缆或者电磁波等真实存在的物理媒介。这些媒介可以传送物理信号&#xff0c;比如亮度、电压或者振幅。对于数字应用来说&#xff0c;我们只需要两种物理信号来分别…

leetcode 435. 无重叠区间

2023.8.3 本题和引爆气球 这题非常类似&#xff0c;利用同样的思路可以解决&#xff0c;代码如下&#xff1a; class Solution { public:static bool cmp(vector<int>& a , vector<int>& b){if(a[0] b[0]) return a[1] < b[1];return a[0] < b[0];…

SpringBoot复习:(16)TomcatStarter

直接在idea里运行SpringBoot程序时&#xff0c;内嵌的tomcat容器会调用TomcatStarter这个类的onStartup方法。TomcatStarter继承自ServletContainerInitializer 其onStartup方法会调用ServletContextInitializer&#xff08;不是ServletContainerInitializer)的onStartup方法.…

Unity 引擎做残影效果——3、顶点偏移方式

Unity实现残影效果 大家好&#xff0c;我是阿赵。 继续讲Unity引擎的残影做法。这次的残影效果和之前两种不太一样&#xff0c;是通过顶点偏移来实现的。 具体的效果是这样&#xff1a; 与其说是残影&#xff0c;这种效果更像是移动速度很快时造成的速度线&#xff0c;所以在移…

关于前后端分离

关于前后端分离 接下来&#xff0c;你将进入 前后端分离项目开发 模块。 这也是企业中比较常见的开发模式。 疑问&#xff1a; 什么是前后端分离&#xff1f;与之前的开发模式有什么区别&#xff1f;企业为什么要用前后端分离&#xff1f; 1. 什么是前后端分离&#xff1f;…

基于人工智能的状态监测帮助结束冷却塔的维护“噩梦”

冷却塔是将水蒸气冷却成较低温的水、将系统的废热排到大气层的排热装置&#xff0c;在工业生产中扮演着不可或缺的关键角色。大型工业冷却塔的主要用途是用来冷却在水冷系统中的循环水。这些水冷系统广泛应用于发电厂、炼油厂、石化厂、天然气制造厂、食品加工厂、半导体厂等工…

ARP断网攻击及防御

ARP断网攻击及防御 攻击防御 攻击 PC1的IP地址 10.9.136.222 PC2的IP地址 10.9.136.55在局域网里通信 需要有IP地址和MAC地址 两台电脑PC1和PC2要想相互通信&#xff0c;PC1在连接PC2的时候&#xff0c;PC1会先查看自己的ARP缓存表&#xff08;命令&#xff1a;arp -a &#xf…

校园跑腿小程序运营攻略

作为一名校园跑腿小程序的运营者&#xff0c;你可能会面临诸如用户获取、平台推广、服务质量保证等挑战。在本篇推文中&#xff0c;我将为你提供一些关键的运营策略&#xff0c;帮助你成功运营校园跑腿小程序。 1. 用户获取和留存 用户是校园跑腿小程序成功的关键。以下是一些…

C++如何用OpenCV中实现图像的边缘检测和轮廓提取?

最近有个项目需要做细孔定位和孔距测量&#xff0c;需要做边缘检测和轮廓提取&#xff0c;先看初步效果图&#xff1a; 主要实现代码&#xff1a; int MainWindow::Test() {// 2.9 单个像素长度um 5倍double dbUnit 2.9/(1000*5);// 定义显示窗口namedWindow("src"…

VR实景导航——开启3D可视化实景导航新体验

数字化时代&#xff0c;我们大家出门在外都是离不开各种导航软件&#xff0c;人们对导航的需求也越来越高&#xff0c;而传统的导航软件由于精度不够&#xff0c;无法满足人们对真实场景的需求&#xff0c;这个时候就需要VR实景导航为我们实景指引目的地的所在。 VR实景导航以其…