GO语言学习(三)

GO语言学习(三)

GO语言的独特接口可以实现内容和面向对象组织的更加方便,我们从这里来详细的讲解接口,让大家感受一下interface的魅力

interface定义

首先接口是一组方法签名的组合,我们通过接口来实现定义对象的一组行为,从这里如果某个对象实现了某个接口所有方法,则称对象实现了此接口,代码如下:

type Human struct {name stringage intphone string
}type Student struct {Human //匿名字段Humanschool stringloan float32
}type Employee struct {Human //匿名字段Humancompany stringmoney float32
}//Human对象实现Sayhi方法
func (h *Human) SayHi() {fmt.Printf("Hi, I am %s you can call me on %s\n", h.name, h.phone)
}// Human对象实现Sing方法
func (h *Human) Sing(lyrics string) {fmt.Println("La la, la la la, la la la la la...", lyrics)
}//Human对象实现Guzzle方法
func (h *Human) Guzzle(beerStein string) {fmt.Println("Guzzle Guzzle Guzzle...", beerStein)
}// Employee重载Human的Sayhi方法
func (e *Employee) SayHi() {fmt.Printf("Hi, I am %s, I work at %s. Call me on %s\n", e.name,e.company, e.phone) //此句可以分成多行
}//Student实现BorrowMoney方法
func (s *Student) BorrowMoney(amount float32) {s.loan += amount // (again and again and...)
}//Employee实现SpendSalary方法
func (e *Employee) SpendSalary(amount float32) {e.money -= amount // More vodka please!!! Get me through the day!
}
// 开始来实现一下接口
// 定义interface,开始讲解咯,大家请注意
type Men interface {SayHi()Sing(lyrics string)Guzzle(beerStein string)
}type YoungChap interface {SayHi()Sing(song string)BorrowMoney(amount float32)
}type ElderlyGent interface {SayHi()Sing(song string)SpendSalary(amount float32)
}

开始解释一下这段代码,友友们可以参考,也可以发评论讨论。

通过上面的代码我们可以知道,interface可以被任意的对象实现。我们看到上面的Men interface被Human、Student和Employee实现。同理,一个对象可以实现任意多个interface,例如上面的Student实现了Men和YoungChap两个interface。

最后,任意的类型都实现了空interface(我们这样定义:interface{}),也就是包含0个method的interface。

interface的值

首先这个interface的值要想了解就必须得知道对象,可以存储实现这个interface的任意类型的对象,例如上面例子中,我们定义了一个Men interface类型的变量m,那么m里面可以存Human、Student或者Employee值,因为m能够持有这三种类型的对象,所以我们可以定义一个包含Men类型元素的slice,这个slice可以被赋予实现了Men接口的任意结构的对象,这个和我们传统意义上面的slice有所不同。

下面直接放代码:

package mainimport "fmt"type Human struct {name stringage intphone string
}type Student struct {Human //匿名字段school stringloan float32
}type Employee struct {Human //匿名字段company stringmoney float32
}//Human实现SayHi方法
func (h Human) SayHi() {fmt.Printf("Hi, I am %s you can call me on %s\n", h.name, h.phone)
}//Human实现Sing方法
func (h Human) Sing(lyrics string) {fmt.Println("La la la la...", lyrics)
}//Employee重载Human的SayHi方法
func (e Employee) SayHi() {fmt.Printf("Hi, I am %s, I work at %s. Call me on %s\n", e.name,e.company, e.phone)}// Interface Men被Human,Student和Employee实现
// 因为这三个类型都实现了这两个方法
type Men interface {SayHi()Sing(lyrics string)
}func main() {mike := Student{Human{"Mike", 25, "222-222-XXX"}, "MIT", 0.00}paul := Student{Human{"Paul", 26, "111-222-XXX"}, "Harvard", 100}sam := Employee{Human{"Sam", 36, "444-222-XXX"}, "Golang Inc.", 1000}tom := Employee{Human{"Tom", 37, "222-444-XXX"}, "Things Ltd.", 5000}//定义Men类型的变量ivar i Men//i能存储Studenti = mikefmt.Println("This is Mike, a Student:")i.SayHi()i.Sing("November rain")//i也能存储Employeei = tomfmt.Println("This is tom, an Employee:")i.SayHi()i.Sing("Born to be wild")//定义了slice Menfmt.Println("Let's use a slice of Men and see what happens")x := make([]Men, 3)//这三个都是不同类型的元素,但是他们实现了interface同一个接口x[0], x[1], x[2] = paul, sam, mikefor _, value := range x{value.SayHi()}
}

通过上面这段代码,你会发现interface就是一组抽象方法的集合,它必须由其他非interface类型实现,而不能自我实现,这点请诸位读者好好理解。

空interface

空interface(interface{})是不包含任何的method的接口,正因为如此,所有的类型都实现了空interface。空interface对于描述起不到任何的作用(因为它不包含任何的method),但是空interface在我们需要存储任意类型的数值的时候相当有用,因为它可以存储任意类型的数值。它有点类似于C语言的void*类型。

代码供参考:

// 定义a为空接口
var a interface{}
var i int = 5
s := "Hello world"
// a可以存储任意类型的数值
a = i
a = s

这个interface中实现一个函数把interface{}作为参数,那么他可以接受任意类型的值作为参数,如果一个函数返回interface{},那么也就可以返回任意类型的值。

interface函数参数

interface的变量可以持有任意实现该interface类型的对象,这给我们编写函数(包括method)提供了一些额外的思考,我们是不是可以通过定义interface参数,让函数接受各种类型的参数,同时你是否注意到它可以接受任意类型的数据,这个可以参考一些实际函数或对象的源文件(源代码)。

这里给个1参考代码:

type stringer_name interface{string()  string
}

也就是实现一段代码我们可以调用所创建的接口来实现,这里我放一段代码,大家可以参考:

package main
import ("fmt""strconv"
)type Human struct {name stringage intphone string
}// 通过这个方法 Human 实现了 fmt.Stringer
func (h Human) String() string {return "❰"+h.name+" - "+strconv.Itoa(h.age)+" years -  ✆ " +h.phone+"❱"
}func main() {Bob := Human{"Bob", 39, "000-7777-XXX"}fmt.Println("This Human is : ", Bob)
}

[!IMPORTANT]

如果需要某个类型能被fmt包以特殊的格式输出,你就必须实现Stringer这个接口。如果没有实现这个接口,fmt将以默认的方式输出。

可以等效成这样子来实现相应功能:

//实现同样的功能
fmt.Println("The biggest one is", boxes.BiggestsColor().String())
fmt.Println("The biggest one is", boxes.BiggestsColor())

interface变量存储的类型

我们来讲解一下这个是到底如何知道这个变量里面保存了哪些类型的对象,一是Comma-ok断言,二是Switch测试。

Comma-ok断言

Go语言里面有一个语法,可以直接判断是否是该类型的变量: value, ok = element.(T),这里value就是变量的值,ok是一个bool类型,element是interface变量,T是断言的类型。如果element里面确实存储了T类型的数值,那么ok返回true,否则返回false。

下面给代码实例:

	package mainimport ("fmt""strconv")type Element interface{}type List [] Elementtype Person struct {name stringage int}//定义了String方法,实现了fmt.Stringerfunc (p Person) String() string {return "(name: " + p.name + " - age: "+strconv.Itoa(p.age)+ " years)"}func main() {list := make(List, 3)list[0] = 1 // an intlist[1] = "Hello" // a stringlist[2] = Person{"Dennis", 70}for index, element := range list {if value, ok := element.(int); ok {fmt.Printf("list[%d] is an int and its value is %d\n", index, value)} else if value, ok := element.(string); ok {fmt.Printf("list[%d] is a string and its value is %s\n", index, value)} else if value, ok := element.(Person); ok {fmt.Printf("list[%d] is a Person and its value is %s\n", index, value)} else {fmt.Printf("list[%d] is of a different type\n", index)}}}

看过之后感觉是不是很简单啊,同时你是否注意到了多个if里面,还记得我前面介绍流程时讲过,if里面允许初始化变量,也许你注意到了,我们断言的类型越多,那么if else也就越多,所以才引出了下面要介绍的switch。

switch测试

下面我来为大家呈现出完整实现代码,并为大家提供解释,大家有啥不会的可以在评论区评论,也可以私信我,让我们一起学习。

	package mainimport ("fmt""strconv")type Element interface{}type List [] Elementtype Person struct {name stringage int}//打印func (p Person) String() string {return "(name: " + p.name + " - age: "+strconv.Itoa(p.age)+ " years)"}func main() {list := make(List, 3)list[0] = 1 //an intlist[1] = "Hello" //a stringlist[2] = Person{"Dennis", 70}for index, element := range list{switch value := element.(type) {case int:fmt.Printf("list[%d] is an int and its value is %d\n", index, value)case string:fmt.Printf("list[%d] is a string and its value is %s\n", index, value)case Person:fmt.Printf("list[%d] is a Person and its value is %s\n", index, value)default:fmt.Println("list[%d] is of a different type", index)}}}

这里有一点需要强调的是:element.(type)语法不能在switch外的任何逻辑里面使用,如果你要在switch外面判断一个类型就使用comma-ok

嵌入interface

下面我们来讲解一下interface的优美用法,让大家一起感受一下go语言的设计魅力,如果一个interface1作为interface2的一个嵌入字段,那么interface2隐式的包含了interface1里面的method。

// 下面我们来看container/heap包的源码
type Interface interface {sort.Interface //嵌入字段sort.InterfacePush(x interface{}) //a Push method to push elements into the heapPop() interface{} //a Pop elements that pops elements from the heap
}
// 我们看到sort.Interface其实就是嵌入字段,把sort.Interface的所有method给隐式的包含进来了。也就是下面三个方法:
type Interface interface {// Len is the number of elements in the collection.Len() int// Less returns whether the element with index i should sort// before the element with index j.Less(i, j int) bool// Swap swaps the elements with indexes i and j.Swap(i, j int)
}
// io包下面的 io.ReadWriter ,它包含了io包下面的Reader和Writer两个interface:
type ReadWriter interface {ReaderWriter
}

反射

所谓反射就是能检查程序在运行时的状态。我们一般用到的包是reflect包。如何运用reflect包

了解reflect包我这里附上了实现reflect包的工作原理,文档参考

使用reflect一般分成三步,下面简要的讲解一下:要去反射是一个类型的值(这些值都实现了空interface),首先需要把它转化成reflect对象(reflect.Type或者reflect.Value,根据不同的情况调用不同的函数)。这两种获取方式如下:

t := reflect.TypeOf(i)    //得到类型的元数据,通过t我们能获取类型定义里面的所有元素
v := reflect.ValueOf(i)   //得到实际的值,通过v我们获取存储在里面的值,还可以去改变值// 转化为reflect对象之后我们就可以进行一些操作了,也就是将reflect对象转化成相应的值
tag := t.Elem().Field(0).Tag  //获取定义在struct里面的标签
name := v.Elem().Field(0).String()  //获取存储在第一个字段里面的值// 获取反射值能返回相应的类型和数值
var x float64 = 3.4
v := reflect.ValueOf(x)
fmt.Println("type:", v.Type())
fmt.Println("kind is float64:", v.Kind() == reflect.Float64)
fmt.Println("value:", v.Float())// 最后,反射的话,那么反射的字段必须是可修改的,我们前面学习过传值和传引用,这个里面也是一样的道理。反射的字段必须是可读写的意思是,如果下面这样写,那么会发生错误
var x float64 = 3.4
v := reflect.ValueOf(x)
v.SetFloat(7.1)// 如果要修改相应的值,必须这样写
var x float64 = 3.4
p := reflect.ValueOf(&x)
v := p.Elem()
v.SetFloat(7.1)

这些只是反射的基本知识,也欢迎大家在评论中讨论共同学习,一起进步。

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

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

相关文章

anaconda创建环境出错HTTPS

报错信息 warnings.warn( /home/ti-3/anaconda3/lib/python3.12/site-packages/urllib3/connectionpool.py:1099: InsecureRequestWarning: Unverified HTTPS request is being made to host ‘repo.anaconda.com’. Adding certificate verification is strongly advised. Se…

Android 自定义SnackBar和下滑取消

如何自定义SnackBar 首先我们得了解SnackBar的布局: 之前我看有一些方案是获取内部的contentLayout,然后做一些处理。但是现在已经行不通了: RestrictTo(LIBRARY_GROUP) public static final class SnackbarLayout extends BaseTransientB…

JavaScript性能优化实战(13):性能测试与持续优化

在前面的系列文章中,我们探讨了各种JavaScript性能优化的方法和实战案例。然而,优化工作不应仅是一次性的努力,而应当成为开发流程中的常态。本篇将聚焦于如何建立系统化的性能测试体系,并实现持续的性能优化机制,确保应用长期保持出色的性能表现。 前端性能测试体系构建…

《告别低效签约!智合同如何用AI重构商业“契约时代”》​​——解析智能合约技术的爆发与行业变革

在数字化浪潮奔涌的当下,合同作为商业活动的核心枢纽,正经历着智能化的深度变革。智合同-合同智能应用这一创新模式,犹如一颗璀璨的新星,在商业领域的天空中绽放出独特光芒,深刻改变着人们对合同管理与应用的认知和实践…

塔防战争:动态寻径与成长系统的控制论架构

目录 塔防战争:动态寻径与成长系统的控制论架构引言第一章 炮塔成长系统1.1 属性升级模型1.2 分支进化树第二章 动态路径规划2.1 JPS优化算法2.2 实时障碍更新第三章 敌人行为系统3.1 多波次生成3.2 智能绕障策略第四章 经济平衡系统4.1 资源流动方程4.2 动态定价模型第五章 特…

快速刷机Android10+Root

说明:仅供学习使用,请勿用于非法用途,若有侵权,请联系博主删除 作者:zhu6201976 一、下载android10源码 1.确认手机可刷机范围 比如我的Piexel3机型,支持刷android9-android12 Android源码。 https://de…

日语学习-日语知识点小记-构建基础-JLPT-N4阶段(24):受身形

日语学习-日语知识点小记-构建基础-JLPT-N4阶段(24):受身形 1、前言(1)情况说明(2)工程师的信仰2、知识点(1)うけみけい 受身形(2)復習(ふくしゅう):3、单词(1)日语(2)日语片假名单词4、相近词练习5、单词辨析记录6、总结1、前言 (1)情况说明 自己在今…

Ankr:Web3基础设施的革新者

在Web3技术蓬勃发展的今天,去中心化基础设施的重要性日益凸显。Ankr作为这一领域的佼佼者,凭借其强大的分布式云计算能力和创新的技术解决方案,正在成为推动Web3发展的关键力量。本文将深入探讨Ankr的技术亮点、应用场景以及其在区块链生态中…

【NLP 75、如何通过API调用智谱大模型】

事事忘记,事事等待,事事自愈 —— 25.5.18 一、调用智谱大模型 zhipuai.model_api.invoke():调用智谱 AI 的大模型(如 ChatGLM)进行文本生成或推理,支持同步请求。 参数列表 参数名类型是否必需默认值说…

【jvm第0集】jvm学习路线指南

jvm学习路线指南 分享jvm学习路线指南,每个模块深入学习都要花费很多时间,但这个是每个java程序员的必修之路,大家可以跟着我的文章学习,可以不精,但必须懂。因为等你需要解决jvm相关问题的时候,你已经知道…

企业终端设备的安全管控

企业终端设备的安全管控是信息安全体系中的重要环节,涉及从设备准入到数据防护的全生命周期管理。 以下是一套系统化的解决方案,涵盖技术、管理和人员三个维度: 一、终端设备全生命周期管控 设备准入控制 802.1X网络认证:对接企业…

Flink SQL 计算实时指标同比的实现方法

在 Flink SQL 中计算实时指标的同比(Year-on-Year),核心是通过时间窗口划分周期(如日、月、周),并关联当前周期与去年同期的指标值。以下是结合流数据处理特性的具体实现方法,包含数据准备、窗口聚合、历史数据关联等关键步骤。 一、同比的定义与场景 同比指当前周期指…

架构的设计

搭建架构的最低前提 1.设计清晰: 需求文档: 有哪些界面 每个界面提够了哪些功能 这些功能是怎样操作的 会有哪些反馈 2.技术: 写架构的同学:这次项目设计的技术 都要有料及(用到的技术有哪些特点 有哪些缺点&…

Mysql varchar类型字段存储的数据末尾带空格,数据过滤不及预期问题

Mysql varchar类型字段存储的数据末尾带空格,数据过滤不及预期问题 引分析 引 最近在项目上遇到一个数据过滤问题,有一批数据,字段末尾多了一个空格,在最后提交的时候被java后端的equals比较校验不匹配,导致业务流程阻…

Debezium TableSchemaBuilder详解

Debezium TableSchemaBuilder详解 1. 类的作用与功能 1.1 核心作用 TableSchemaBuilder是Debezium中负责构建表Schema的核心类,主要功能包括: Schema构建:将数据库表结构转换为Kafka Connect的Schema定义主键处理:生成表的主键Schema值Schema处理:生成表的非主键字段Sc…

P1090 [NOIP 2004 提高组] 合并果子

P1090 [NOIP 2004 提高组] 合并果子 - 洛谷 #include <bits/stdc.h> using namespace std; int main() {int n;cin >> n;priority_queue<int, vector<int>, greater<int>> pq; //小根堆for (int i 1; i < n; i) {int x;cin >> x;pq.pu…

《量子雷达》学习(1) 2025.5.20

《量子雷达》深度解析&#xff1a;理论、技术与未来启示 一、书籍核心内容与理论框架 Marco Lanzagorta的《量子雷达》从量子力学与经典电磁理论的融合视角出发&#xff0c;系统构建了量子雷达探测的理论体系。全书以量子态的传输、散射与检测为核心&#xff0c;重新定义了雷…

支持向量存储:PostgresSQL及pgvector扩展详细安装步骤!老工程接入RAG功能必备!

之前文章和大家分享过&#xff0c;将会出一篇专栏&#xff08;从电脑装ubuntu系统&#xff0c;到安装ubuntu的常用基础软件&#xff1a;jdk、python、node、nginx、maven、supervisor、minio、docker、git、mysql、redis、postgresql、mq、ollama等&#xff09;&#xff0c;目前…

uni-app(2):页面

1 页面简介 uni-app项目中&#xff0c;一个页面就是一个符合Vue SFC规范的 vue 文件。 在 uni-app js 引擎版中&#xff0c;后缀名是.vue文件或.nvue文件。 这些页面均全平台支持&#xff0c;差异在于当 uni-app 发行到App平台时&#xff0c;.vue文件会使用webview进行渲染&…

Unity异步加载image的材质后,未正确显示的问题

简述&#xff1a; 此问题涉及到Unity的UI刷新机制 问题描述&#xff1a; 如图所示&#xff0c;想要实现在打开新的界面时候&#xff0c;通过修改材质的方式&#xff0c;修改image的显示内容。 明明已经给image添加上材质了&#xff0c;可还是一片空白&#xff1f; 先看看代…