十一、快速入门go语言之接口和反射
文章目录
- 接口
- :one: 接口基础
- :two: 接口类型断言和空接口
- :star2: 空接口实现存储不同数据类型的切片/数组
- :star2: 复制切片到空接口切片
- :star2: 类型断言
- 反射
📅 2024年5月9日
📦 使用版本为1.21.5
接口
十、Java类的封装和继承、多态 - 七点半的菜市场 (tanc.fun) Java的接口
ps: 我感觉Go的接口和类方法两个就很模糊
1️⃣ 接口基础
⭐️ 接口就是一些未实现功能的集合(我是这样理解的),为了实现多态(就是多状态)
type PersonIF interface {Status() //人类的状态Skills() string //会的技能
}
⭐️ 在Go语言中接口可以有值,它的值默认是nil,接口本质上是一个指针,一个类型如果实现了接口中的所有方法,那么这个类实现的方法就会使用指针自动指向接口的方法
⚠️ 注意是所有抽象方法,如果有一个没实现都会报错
⭐️ 类型不需要使用什么关键字来表面实现了这个接口,多个类型可以同时实现一个接口,一个类型也可以实现多个接口
type PersonIF interface {Status()Skills()
}type WorkIF interface {Class()Income()
}type Person struct { //人的基本Name stringage intsex string
}/*Person实现PersonIF接口
*/
func (this *Person) Status() {println("Status")
}func (this *Person) Skills() {println("Skills")
}/*Person实现WorkIF接口*/
func (this *Person) Class() {println("Class")
}
func (this *Person) Income() {println("Income")
}
⭐️ 调用则和Java差不多,这里可以直接使用Interfer来创建一个变量调用,然后就可以调用实现interfer内的方法
type PersonIF interface {Status()Skills()
}type WorkIF interface {Class()Income()
}type Person struct { //人的基本Name stringage intsex string
}/*
Person实现PersonIF接口
*/
func (this *Person) Status() {println("Status")
}func (this *Person) Skills() {println("Skills")
}/*
Person实现WorkIF接口
*/
func (this *Person) Class() {println("Class")
}
func (this *Person) Income() {println("Income")
}//Person类自己的方法
func (this *Person) GetTest() {println("GetTest")
}func main() {p1 := Person{"张三", 18, "男"} //创建一个对象pIn := PersonIF(&p1) //实现接口pIn.Status() //调用接口的方法,接口无法调用类自己的方法,也就是无法调用GetTestpIn.Skills()p1.GetTest()fmt.Println(pIn) //接口变量包含了接受实列的值和指向对应方法表的指针,这里输出的是实列的值
}
⭐️ 接口也可以内嵌接口
type PersonIF interface {Status()Skills()WorkIF
}type WorkIF interface {Class()Income()
}
func main() {p1 := Person{"张三", 18, "男"} //创建一个对象pIn := PersonIF(&p1) //实现接口pIn.Class() //可以调用嵌套接口中的实现fmt.Println(pIn)
}
2️⃣ 接口类型断言和空接口
⭐️ Go语言有个万能的类型通配符,是一个空接口interfer{},因为在Go中任何接口类型都实现了空接口,类似于Java中的Object
⭐️ 每个空接口变量在内存中占据两个字长,一个是用来存储包含的类型,一个是存储数据或者指向数据的指针
func main() {Test(1)Test("abc")Test(1.111)
}func Test(i interface{}) { //空类型fmt.Println(i)
}//输出:
1
abc
1.111
🌟 空接口实现存储不同数据类型的切片/数组
⭐️ 直接使用type给空接口一个别名类型,然后创建这个接口别名类型的切片/数组(真的太聪明了这个办法)
package mainimport "fmt"type Empty interface{}type Vector struct {e []Empty
}func (this *Vector) NewInit() { //初始化切片this.e = make([]Empty, 5)
}func (this *Vector) Set(i int, a interface{}) { //将元素插入指定索引this.e[i] = a
}
func (this *Vector) Get(i int) { //获取指定索引元素并输出fmt.Println(this.e[i])
}func main() {var v Vectorv.NewInit() //初始化v.Set(0, 22) //整数v.Set(1, "222") //stringv.Set(2, 13.14) //float32v.Get(0)v.Get(1)v.Get(2)
}//输出:
22
222
13.14
🌟 复制切片到空接口切片
⭐️ 如果你需要将切片复制到一个空接口切片中需要通过for-range,不能直接传递
func main() {dataSlice := make([]int, 0)dataSlice = append(dataSlice, 1, 2, 3, 4, 5, 6)emptySlice := make([]interface{}, 10)for i, j := range dataSlice {emptySlice[i] = j}fmt.Println(emptySlice)
}
⭐️ 一个接口的值是可以赋值给另外一个接口变量,只要底层类型实现了必要的方法
🌟 类型断言
⭐️ 如果变量是一个接口变量,则直接可以使用断言机制,直接就是接口变量.(类型)即可,注意一定要是接口变量
func main() {Test(1)Test("abc")
}func Test(i interface{}) { //空类型if value, ok := i.(string); ok {fmt.Printf("值: %v 是string\n", value)} else {fmt.Printf("值: %v 不是string\n", i)}
}
⭐️ 还有一种是type-switch
func main() {Test(1)Test("abc")
}func Test(i interface{}) { //空类型switch v := i.(type) {case string:fmt.Printf("值 %v 是string\n", v)case int:fmt.Printf("值 %v 是int\n", v)case float32:fmt.Printf("值 %v 是float32\n", v)default:fmt.Printf("我不想学习\n")}
}
反射
Go 语言反射的实现原理 | Go 语言设计与实现 (draveness.me) 可以好好看看
⭐️ Go语言的反射机制也是通过空接口来实现的,在reflect包中
⭐️ 反射提供了两个简单的函数实现一个是TypeOf(获取类型信息),一个是ValueOf(获取数据的运行时表示)
它们两个对应了两个不同的接口分别是Type和Value

⭐️ TypeOf返回的是一个Type接口对象,所以说它可以使用Type方法
它接受一个any类型的值
type any = interface{} //空接口
// TypeOf函数返回给定参数i的类型。
//
// 参数:
// i - 任意类型的值。
//
// 返回值:
// Type - 表示参数i类型的类型值。
func TypeOf(i any) Type {// 将i转换为emptyInterface类型,以获取其动态类型信息。eface := *(*emptyInterface)(unsafe.Pointer(&i))// 使用noescape函数确保i的指针不会逃逸,这样做是为了避免i的生命周期被延长。// 这是基于Value.typ字段的注释中提到的安全理由。return toType((*abi.Type)(noescape(unsafe.Pointer(eface.typ))))
}
⭐️ 在Type接口中内定义了很多方法
type Type interface {// 基本属性Align() int // 返回类型值对齐所需的字节数。FieldAlign() int // 返回类型作为结构体字段时的对齐要求。// 方法相关Method(int) Method // 获取方法集合中的第i个方法。NumMethod() int // 返回方法总数。MethodByName(string) (Method, bool) // 通过名称查找方法。// 类型基本信息Name() string // 返回类型名。PkgPath() string // 返回类型所在的包路径。Size() uintptr // 返回类型的大小。String() string // 返回类型表示的字符串。Kind() Kind // 返回类型的基本类别。// 类型兼容性Implements(Type) bool // 检查是否实现某个接口。AssignableTo(Type) bool // 检查值是否可直接赋值给另一类型。ConvertibleTo(Type) bool // 检查值是否可通过类型转换匹配另一类型。Comparable() bool // 指示类型值是否可比较。// 特定类型特有操作Elem() Type // 对数组/切片/指针/映射/通道,返回元素类型。ChanDir() ChanDir // 对通道类型,返回通信方向。IsVariadic() bool // 对函数类型,检查是否可变参数。Key() Type // 对映射类型,返回键的类型。Len() int // 对数组类型,返回其长度。// 结构体操作Field(int) StructField // 获取结构体的第i个字段信息。NumField() int // 返回结构体字段数量。FieldByIndex([]int) StructField // 通过嵌套索引获取字段信息。FieldByName(string) (StructField, bool) // 通过字段名获取字段信息。FieldByNameFunc(func(string) bool) (StructField, bool) // 通过匹配函数查找字段。// 函数类型操作In(int) Type // 获取函数的第i个输入参数类型。NumIn() int // 返回函数输入参数数量。Out(int) Type // 获取函数的第i个输出参数类型。NumOut() int // 返回函数输出参数数量。// 内部方法,用户通常无需直接调用common(), uncommon() *internal // 返回底层实现相关的数据结构。
}
⭐️ ValueOf 放回一个Value对象,接受的也是一个任意类型的值
// ValueOf 是一个将任意类型转换为 Value 类型的函数。
// 参数 i 为任意类型的值,表示需要转换的值。
// 返回值为 Value 类型,表示转换后的值。
// 如果输入值 i 为 nil,则返回一个空的 Value。
func ValueOf(i any) Value {// 检查输入值是否为 nil,如果是则返回空的 Valueif i == nil {return Value{}}// 如果满足特定条件(go121noForceValueEscape 为 false),则标记 i 为逃逸变量if !go121noForceValueEscape {escapes(i)}// 将输入值 i 解包为 Value 类型并返回return unpackEface(i)
}
⭐️ 在Value中也定义了一些方法,反射包中 reflect.Value 的类型与 reflect.Type 不同,它被声明成了结构体。这个结构体没有对外暴露的字段,但是提供了获取或者写入数据的方法
type Value struct {// 内部结构未公开,实际包含值和类型等信息
}// 实例化Value的方法通常通过reflect.ValueOf或reflect零值的间接操作获得// Kind 返回此Value所持有的值的类型种类。
func (v Value) Kind() Kind// Type 返回此Value所持有的值的具体类型信息。
func (v Value) Type() Type// Bool 获取bool类型的值,如果Value不是bool类型则会panic。
func (v Value) Bool() bool// Int 获取整数值,类型必须是整数类型,否则会panic。
func (v Value) Int() int64// Float 获取浮点数值,类型必须是浮点数类型,否则会panic。
func (v Value) Float() float64// String 获取字符串值,类型必须是string,否则会panic。
func (v Value) String() string// Interface 转换Value为interface{}类型,几乎所有类型都可以通过此方法获取。
func (v Value) Interface() interface{}// Set 设置Value的值,新值必须是可设置的并且类型匹配。
func (v Value) Set(x Value) // SetBool 设置bool类型的值,Value必须是可设置的bool类型。
func (v Value) SetBool(x bool)// SetInt 设置整数值,Value必须是可设置的整数类型。
func (v Value) SetInt(x int64)// SetFloat 设置浮点数值,Value必须是可设置的浮点数类型。
func (v Value) SetFloat(x float64)// SetString 设置字符串值,Value必须是可设置的string类型。
func (v Value) SetString(x string)// Call 对于函数或方法Value,执行调用并返回结果。
func (v Value) Call(in []Value) []Value// Elem 如果Value是一个指针,返回它指向的值的Value;否则返回自身。
func (v Value) Elem() Value// Field 获取结构体Value的第i个字段的Value。
func (v Value) Field(i int) Value// FieldByName 获取结构体Value的名为name的字段的Value。
func (v Value) FieldByName(name string) Value// Index 对于数组、slice或map的Value,返回索引i处的元素Value。
func (v Value) Index(i int) Value// MapIndex 对于map的Value,返回key对应的元素Value。
func (v Value) MapIndex(key Value) Value// CanSet 返回此Value是否可被设置,即是否可以更改其底层值。
func (v Value) CanSet() bool// IsZero 判断Value的底层值是否为零值。
func (v Value) IsZero() bool
⭐️ Type方法实列
type Person struct {Name stringAge intSex string
}func (this *Person) toString() string {return fmt.Sprintf("Nmae: %v,Age: %v,Sex: %v", this.Name, this.Age, this.Sex)
}func main() {p1 := Person{"张三", 18, "男"}p1_demo := reflect.TypeOf(p1) //获取类型信息fmt.Println(p1_demo.Name()) //输出类名fmt.Println(p1_demo.Size()) //输出类型大小for i := 0; i < p1_demo.NumField(); i++ { //放回结构体字段的个数,然后输出遍历field := p1_demo.Field(i) //获取字段fmt.Println(field.Name, field.Type, field.Offset)}for i := 0; i < p1_demo.NumMethod(); i++ { //放回结构体方法的个数,然后输出遍历method := p1_demo.Method(i) //获取方法fmt.Println(method.Name, method.Type)}//数组p2 := [1]Person{Person{"张三", 18, "男"}}p2_demo := reflect.TypeOf(p2)fmt.Println(p2_demo.Len()) //输出数组长度}
⭐️ Value方法实现,可以使用ValueOf方法来修改值
package mainimport ("fmt""reflect"
)type Person struct {Name stringAge intSex string
}func main() {// 使用指针来确保可以通过反射修改值p1 := Person{"张三", 18, "男"}fmt.Println(p1)// 获取p1的反射值p1_demo := reflect.ValueOf(&p1)// 由于p1是指针,我们先获取其指向的值的反射值p1_val := p1_demo.Elem()// 检查是否可以设置值if p1_val.CanSet() {// 创建一个新的Person值并通过反射设置newPerson := Person{"李四", 20, "女"}p1_val.Set(reflect.ValueOf(newPerson))} else {fmt.Println("无法设置值")}// 打印修改后的值fmt.Printf("%#v\n", p1) // 使用%#v格式化输出结构体细节fmt.Println(p1_demo.Kind()) // 输出类型信息
}
欢迎关注我,继续探讨技术,如果觉得写的不错动动小手点个小赞,如果觉得我还有哪些不足可以私信哦
相关文章:
十一、快速入门go语言之接口和反射
文章目录 接口:one: 接口基础:two: 接口类型断言和空接口:star2: 空接口实现存储不同数据类型的切片/数组:star2: 复制切片到空接口切片:star2: 类型断言 反射 📅 2024年5月9日 📦 使用版本为1.21.5 接口 十、Java类的封装和继承、多态 - 七点半的菜市…...
智能化图书馆导航系统方案之系统架构与核心功能设计
hello~这里是维小帮,点击文章最下方获取图书馆导航系统解决方案!如有项目需求和技术交流欢迎大家私聊我们~撒花! 针对传统图书馆在图书查找困难、座位紧张、空间导航不便方面的问题,本文深入剖析了基于高精度定位、3D建模、图书搜…...
学习嵩山版《Java 开发手册》:编程规约 - 命名风格(P13 ~ P14)
概述 《Java 开发手册》是阿里巴巴集团技术团队的集体智慧结晶和经验总结,他旨在提升开发效率和代码质量 《Java 开发手册》是一本极具价值的 Java 开发规范指南,对于提升开发者的综合素质和代码质量具有重要意义 学习《Java 开发手册》是一个提升 Jav…...
Qt关于padding设置不起作用的的解决办法
观察以下的代码: MyWidget::MyWidget(QWidget *parent): QWidget{parent},m_btn(new QToolButton(this)) {this->setFixedSize(500,500);m_btn->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);m_btn->setIcon(QIcon("F:tabIcon/person-white.s…...
Golang教程第10篇(语言循环语句-语言循环嵌套)
Go 语言循环嵌套 Go 语言循环语句Go 语言循环语句 Go 语言允许用户在循环内使用循环。接下来我们将为大家介绍嵌套循环的使用。 语法 以下为 Go 语言嵌套循环的格式: for [condition | ( init; condition; increment ) | Range] {for [condition | ( init; con…...
Python Web 开发:FastAPI 入门实战 —— HTTP 基础与 RESTful API 设计
Python Web 开发:FastAPI 入门实战 —— HTTP 基础与 RESTful API 设计 目录 🚀 HTTP 协议概述🌐 HTTP 请求与响应的工作原理🛠️ RESTful API 设计理念🗂️ JSON 格式数据的传输与解析 1. 🚀 HTTP 协议概…...
uniapp实现组件竖版菜单
社区图片页面 scroll-view scroll-view | uni-app官网 (dcloud.net.cn) 可滚动视图区域。用于区域滚动。 需注意在webview渲染的页面中,区域滚动的性能不及页面滚动。 <template><view class"pics"><scroll-view class"left"…...
osg、osgearth源码编译(二)
如果比较懒,也可以不看这篇文章,网上应该有很多编译好的库。也可以找我要。 本人还是建议学会编译,因为其他人电脑上编译好的,可能在你的电脑环境上,出现这样那样奇怪的问题,所以,最好还是自己能…...
从单一设备到万物互联:鸿蒙生态崛起的未来之路
目录 一、引言:开启智能时代的钥匙 二、鸿蒙生态概述:跨设备协同的核心价值 三、开发者机遇与挑战:抓住鸿蒙崛起的机会 四、鸿蒙生态崛起的前景:万物互联的未来 五、开发者在鸿蒙生态中的实践机遇与挑战 1. 跨设备开发的机遇…...
Kotlin的object修饰符定义类似Java的静态类/静态方法
Kotlin的object修饰符定义类似Java的静态类/静态方法 //类似Java的static类 object StaticCls {//类似Java静态变量private var num 0//类似Java的静态方法fun updateVal(n: Int) {num n}fun getVal(): Int {return num} }class MyTest() {fun setVal() {StaticCls.updateVal…...
html 中的 <code>标签
定义和用途 <code> 标签是HTML中的一个内联元素,用于定义计算机代码片段。当你需要在网页内容中展示代码,比如编程语言代码(如JavaScript、Python、Java等)、命令行指令、标记语言代码(如HTML、XML等)…...
【Oracle11g SQL详解】GROUP BY 和 HAVING 子句:分组与过滤
GROUP BY 和 HAVING 子句:分组与过滤 在 Oracle 11g 中,GROUP BY 子句用于根据一个或多个列对查询结果进行分组,而 HAVING 子句用于对分组后的结果进行过滤。这两者常结合聚合函数使用,用以实现复杂的数据统计和分析。本文将系统…...
SSE基础配置与使用
什么是 Server-Sent Events (SSE) **Server-Sent Events (SSE) **是一种轻量的服务器向客户端推送消息的机制,基于 HTTP 协议实现单向通信,适用于需要实时更新的场景。 与 WebSocket 不同,SSE 只允许服务器向客户端发送数据,因此…...
Android -- 简易音乐播放器
Android – 简易音乐播放器 播放器功能:* 1. 播放模式:单曲、列表循环、列表随机;* 2. 后台播放(单例模式);* 3. 多位置同步状态回调;处理模块:* 1. 提取文件信息:音频文…...
【开源免费】基于Vue和SpringBoot的技术交流分享平台(附论文)
博主说明:本文项目编号 T 053 ,文末自助获取源码 \color{red}{T053,文末自助获取源码} T053,文末自助获取源码 目录 一、系统介绍二、演示录屏三、启动教程四、功能截图五、文案资料5.1 选题背景5.2 国内外研究现状5.3 可行性分析…...
Python异步编程新写法:asyncio模块的最新实践
Python异步编程新写法:asyncio模块的最新实践 引言1. 异步编程基础2. 旧写法的问题3. 最新的写法4. 代码解析5. 最佳实践6. 总结7. 参考资料 引言 在现代编程中,异步编程已经成为提高程序性能和响应能力的重要手段。Python的asyncio模块为开发者提供了一…...
【Docker】Docker配置远程访问
配置Docker的远程访问,你需要按照以下步骤进行操作: 1. 在Docker宿主机上配置Docker守护进程监听TCP端口 Docker守护进程默认只监听UNIX套接字,要实现远程访问,需要修改配置以监听TCP端口。 方法一:修改Docker服务…...
网络安全入门之网络安全工具分享-含初期所有工具(附百度网盘链接)
网络安全基础工具 抓包工具 burpsuite 这是一款十分经典的抓包改包工具,在全球范围内使用十分广泛,并且其内置各种插件,具有爆破,自动识别验证码,加解密发包等多种功能 专业版破解网盘链接: 通过百度网…...
玩转 uni-app 静态资源 static 目录的条件编译
一. 前言 老生常谈,了解 uni-app 的开发都知道,uni-app 可以同时支持编译到多个平台,如小程序、H5、移动端 App 等。它的多端编译能力是 uni-app 的一大特点,让开发者可以使用同一套代码基于 Vue.js 的语法编写程序,然…...
Docker 容器隔离关键技术:Seccomp
Docker 容器隔离关键技术:Seccomp 在 Docker 容器中,Seccomp(Secure Computing Mode) 是一种内核安全机制,用来限制容器内的程序可以调用哪些系统调用(Syscalls)。通过列清单的方式,…...
【大模型RAG】拍照搜题技术架构速览:三层管道、两级检索、兜底大模型
摘要 拍照搜题系统采用“三层管道(多模态 OCR → 语义检索 → 答案渲染)、两级检索(倒排 BM25 向量 HNSW)并以大语言模型兜底”的整体框架: 多模态 OCR 层 将题目图片经过超分、去噪、倾斜校正后,分别用…...
装饰模式(Decorator Pattern)重构java邮件发奖系统实战
前言 现在我们有个如下的需求,设计一个邮件发奖的小系统, 需求 1.数据验证 → 2. 敏感信息加密 → 3. 日志记录 → 4. 实际发送邮件 装饰器模式(Decorator Pattern)允许向一个现有的对象添加新的功能,同时又不改变其…...
【力扣数据库知识手册笔记】索引
索引 索引的优缺点 优点1. 通过创建唯一性索引,可以保证数据库表中每一行数据的唯一性。2. 可以加快数据的检索速度(创建索引的主要原因)。3. 可以加速表和表之间的连接,实现数据的参考完整性。4. 可以在查询过程中,…...
《通信之道——从微积分到 5G》读书总结
第1章 绪 论 1.1 这是一本什么样的书 通信技术,说到底就是数学。 那些最基础、最本质的部分。 1.2 什么是通信 通信 发送方 接收方 承载信息的信号 解调出其中承载的信息 信息在发送方那里被加工成信号(调制) 把信息从信号中抽取出来&am…...
【python异步多线程】异步多线程爬虫代码示例
claude生成的python多线程、异步代码示例,模拟20个网页的爬取,每个网页假设要0.5-2秒完成。 代码 Python多线程爬虫教程 核心概念 多线程:允许程序同时执行多个任务,提高IO密集型任务(如网络请求)的效率…...
React---day11
14.4 react-redux第三方库 提供connect、thunk之类的函数 以获取一个banner数据为例子 store: 我们在使用异步的时候理应是要使用中间件的,但是configureStore 已经自动集成了 redux-thunk,注意action里面要返回函数 import { configureS…...
快刀集(1): 一刀斩断视频片头广告
一刀流:用一个简单脚本,秒杀视频片头广告,还你清爽观影体验。 1. 引子 作为一个爱生活、爱学习、爱收藏高清资源的老码农,平时写代码之余看看电影、补补片,是再正常不过的事。 电影嘛,要沉浸,…...
【WebSocket】SpringBoot项目中使用WebSocket
1. 导入坐标 如果springboot父工程没有加入websocket的起步依赖,添加它的坐标的时候需要带上版本号。 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-websocket</artifactId> </dep…...
【Linux】Linux安装并配置RabbitMQ
目录 1. 安装 Erlang 2. 安装 RabbitMQ 2.1.添加 RabbitMQ 仓库 2.2.安装 RabbitMQ 3.配置 3.1.启动和管理服务 4. 访问管理界面 5.安装问题 6.修改密码 7.修改端口 7.1.找到文件 7.2.修改文件 1. 安装 Erlang 由于 RabbitMQ 是用 Erlang 编写的,需要先安…...
高防服务器价格高原因分析
高防服务器的价格较高,主要是由于其特殊的防御机制、硬件配置、运营维护等多方面的综合成本。以下从技术、资源和服务三个维度详细解析高防服务器昂贵的原因: 一、硬件与技术投入 大带宽需求 DDoS攻击通过占用大量带宽资源瘫痪目标服务器,因此…...
