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

news2024/12/26 11:05:33

反射

反射的存在意义

在开发中,你或许会碰到在有些情况下,你需要获取一个对象的类型,属性及方法,而这个过程其实就是反射。
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.Reader

tty, 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 uint

const (
  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.Profile
       Kind  struct */
    fmt.Println("Type:",t)
	//1.传入值
	fmt.Println("Kind ",t.Kind())

	//2.传入指针
	v := reflect.ValueOf(&m)
	/* &m Type : *main.Profile
       &m Kind : ptr
	   m Type : main.Profile
	   m 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 main

import (
    "fmt"
    "reflect"
)

func main() {
    var age int = 25
    v1 := 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())
   //Field
   fmt.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 string
	age int
	gender string
}

func(p Pearson)SayBye(){
	fmt.Println("Bye")
}

func(p Pearson)SayHello(){
	fmt.Println("Hello")
}

func main() {
   p := &Pearson{"xy",21,"male"}
//注意使用 TypeOf
   v:=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.coloradmin.cn/o/832941.html

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

相关文章

【智慧校园】智慧班牌云平台源码,使用springboot vue框架

【智慧校园】智慧班牌云平台源码&#xff0c;使用springboot vue框架 前后端分离架构 1、使用springboot vue框架 2、数据库MySQL5.7 3、移动端小程序使用小程序原生语言开发 4、电子班牌固件安卓7.1&#xff1b;使用Java Android原生 5、java&#xff0c;elmentui &…

四、Unity中颜色空间

Unity中的设置 通过点击菜单Edit->Project Settings->Player页签->Other Settings下的Rendering部分进行修改&#xff0c;参数Color Space可以选择Gamma或Linear。 当选择Gamma Space时&#xff0c;Unity不会做任何处理。当选择Linear Space时&#xff0c;引擎的渲染…

电测知识分享——三千字解读时钟电路最重要部件,建议收藏!

晶振作为时钟电路中最重要的部件&#xff0c;在电路中起着产生震荡频率的作用&#xff0c;可以产生高度稳定的信号&#xff0c;并稳定工作环境&#xff0c;为系统提供基本的时钟信号&#xff0c;在工业、科技、车载、数码、电子等多个领域几乎都有应用场景。 今天&#xff0c;…

MySQL binLog问题

看到数据库目录下有很多OFF.*文件的时候很诧异&#xff0c;这玩意是啥&#xff0c;binlog不应该都是*bin-log.*​的文件吗&#xff1f;* [roottest ~]# cd /data/mysql_data [roottest mysql_data]# ls ansible hap_attach_yl hap_func_yl hap_msg_yl h…

区块链媒体发稿:区块链媒体宣发常见问题解析

据统计&#xff0c;由于区块链应用和虚拟货币的兴起&#xff0c;越来越多媒体对区块链领域开展报导&#xff0c;特别是世界各国媒体宣发全是热火朝天。但是&#xff0c;随着推卸责任媒体宣发的五花八门&#xff0c;让很多人因而上当受骗&#xff0c;乃至伤害一大笔资产。身为投…

【TiDB理论知识08】HATP概述

1 HTAP技术 OLTP 在线事务 支付 转账 高并发 每次操作的数据量少 &#xff0c;行存 OLAP 报表分析 每次操作大量数据 列存储 2 传统解决方案 数据抽取到数仓或者数据湖 ETL有延迟 &#xff0c;一般会有T1 T2 数据多副本 3 HTAP的要求 4 TIDB的HTAP架构 TiFlash特点&…

IO进程线程day6(2023.8.3)

一、Xmind整理&#xff1a; 进程与线程关系&#xff1a; 二、课上练习&#xff1a; 练习1&#xff1a;pthread_create 功能&#xff1a;创建一个线程 原型&#xff1a; #include <pthread.h> int pthread_create(pthread_t *thread, const pthread_attr_t *attr, vo…

【新版系统架构补充】-七层模型

网络功能和分类 计算网络的功能 &#xff1a;数据通信、资源共享、管理集中化、实现分布式处理、负载均衡 网络性能指标&#xff1a;速率、带宽&#xff08;频带宽度或传送线路速率&#xff09;、吞吐量、时延、往返时间、利用率 网络非性能指标&#xff1a;费用、质量、标准化…

三、基本流程控制结构

3.1结构化程序设计 基本控制结构&#xff1a; 顺序结构选择结构循环结构 C语句&#xff1a; 说明语句控制语句函数调用语句表达式语句空语句复合语句 3.2选择结构语句 if语句&#xff1a; &#xff08;1&#xff09;单选条件语句 if(表达式) 语句 if(x>y) cout<&l…

Tomcat的介绍和安装配置、eclipse中动态web项目的创建和运行、使用IDEA创建web项目并运行

一、Tomcat的介绍和安装配置 安装tomcat&#xff1a; 环境变量的配置&#xff1a; 配置之后重启cmd&#xff0c;执行startup命令&#xff0c;启动tomcat 在localhost:8080&#xff0c;能进入tomcat主界面&#xff0c;说明配置成功 二、eclipse中动态web项目的创建和运行 tomca…

基于回溯算法实现八皇后问题

八皇后问题是一个经典的计算机科学问题&#xff0c;它的目标是将8个皇后放置在一个大小为88的棋盘上&#xff0c;使得每个皇后都不会攻击到其他的皇后。皇后可以攻击同一行、同一列和同一对角线上的棋子。 一、八皇后问题介绍 八皇后问题最早由国际西洋棋大师马克斯贝瑟尔在18…

王道《操作系统》学习(二)—— 进程管理(二)

2.1 处理机调度的概念、层次 2.1.1 调度的基本概念 2.1.2 调度的三个层次 &#xff08;1&#xff09;高级调度&#xff08;作业调度&#xff09; &#xff08;2&#xff09;中级调度&#xff08;内存调度&#xff09; 补充知识&#xff1a;进程的挂起状态和七状态模型 &#x…

Three.js室内场景

Three.js实现三维可视化室内场景 1.效果 2.安装 要安装three 的 npm 模块,请在你的项目文件夹里打开终端窗口,并运行: npm install three 或 yarn add three包将会被下载并安装。然后你就可以将它导入你的代码了: import * as THREE from three引入性能监视器: impor…

MySQL日志——错误日志、二进制日志

错误日志二进制日志查询日志慢查询日志 1.错误日志 查看日志位置&#xff1a; show variables like %log_error%查看错误日志&#xff1a; tail -f /var/log/mysql.log2.二进制日志 show variables like %log_bin%;cd /var/lib/mysql ll2.1 日志格式 查看日志格式指令&…

第9章 CSS-DOM

三位一体的网页 游览器由结构层&#xff0c;表现层&#xff0c;行为层组成 结构层 网页的结构层&#xff08;structural layer&#xff09;由HTML或XHTML之类的标记语言负责创建。 表现层 表示层&#xff08;presentation layer&#xff09;由CSS负责完成。CSS描述页面内容…

腾讯云COS+PicGO+截图工具+Obsidian+Typora+蚁小二:打造丝滑稳定的Markdown写作和分发环境

背景 很久很久以前&#xff0c;我写过一篇《有道云笔记EverythingTyporaGitHub图床PicGojsDelivr加速截图工具——创造丝滑免费的Markdown写作环境》&#xff08;https://blog.csdn.net/qq_43721542/article/details/9685957&#xff09;&#xff0c;当时的目的是打造一个云同…

中国艺术孙溟㠭篆刻作品《活着》

人人为生活挣扎着&#xff0c;做着不想做的事&#xff0c;说着不想说的话&#xff0c;为生活低头弯腰委屈求全人生苦多甜少&#xff0c;何时了&#xff01;何时了&#xff01;甜来人生到头了…… 孙溟㠭篆刻作品《活着》 孙溟㠭篆刻作品《活着》 孙溟㠭篆刻作品《活着》 文/九钵

美团基础架构面经总结汇总

美团基础架构的面经。 问的全是基础,一个编程语言的问都没有。 问题记录 MySQL-MVCC InooDB是通过 MVCC 实现可重复读的隔离级别的,MVCC 就是多版本并发控制,它其实记录了历史版本的数据,解决了读写并发冲突问题。有一个版本编码,然后它进入了各种操作下的数据状态,能…

2023华数杯数学建模A题思路 - 隔热材料的结构优化控制研究

# 1 赛题 A 题 隔热材料的结构优化控制研究 新型隔热材料 A 具有优良的隔热特性&#xff0c;在航天、军工、石化、建筑、交通等 高科技领域中有着广泛的应用。 目前&#xff0c;由单根隔热材料 A 纤维编织成的织物&#xff0c;其热导率可以直接测出&#xff1b;但是 单根隔热…

JDK19 - 虚拟线程详解

JDK19 - 虚拟线程详解 前言一. Continuation 和 虚拟线程1.1 Continuation 案例1.2 Continuation 内的重要成员1.3 run() 执行/恢复执行1.4 yield() 暂停执行1.5 测试和小总结 二. VirtualThread 解读2.1 VirtualThread 内的重要成员和构造2.2 VirtualThread 的首次执行2.3 结束…