《Go 语言第一课》课程学习笔记(十三)

news2024/11/23 11:08:19

方法

认识 Go 方法

  • Go 语言从设计伊始,就不支持经典的面向对象语法元素,比如类、对象、继承,等等,但 Go 语言仍保留了名为“方法(method)”的语法元素。当然,Go 语言中的方法和面向对象中的方法并不是一样的。Go 引入方法这一元素,并不是要支持面向对象编程范式,而是 Go 践行组合设计哲学的一种实现层面的需要。
    在这里插入图片描述
    • Go 的方法也是以 func 关键字修饰的,并且和函数一样,也包含方法名(对应函数名)、参数列表、返回值列表与方法体(对应函数体)。
    • 方法中的这几个部分和函数声明中对应的部分,在形式与语义方面都是一致的,比如:方法名字首字母大小写决定该方法是否是导出方法;方法参数列表支持变长参数;方法的返回值列表也支持具名返回值等。
    • 和由五个部分组成的函数声明不同,Go 方法的声明有六个组成部分,多的一个就是图中的 receiver 部分。在 receiver 部分声明的参数,Go 称之为 receiver 参数,这个 receiver 参数也是方法与类型之间的纽带,也是方法与函数的最大不同。
      • Go 中的方法必须是归属于一个类型的,而 receiver 参数的类型就是这个方法归属的类型,或者说这个方法就是这个类型的一个方法。
      • 这里的 receiver 参数 srv 的类型为 *Server,那么我们可以说,这个方法就是 *Server 类型的方法。
      • 每个方法只能有一个 receiver 参数,Go 不支持在方法的 receiver 部分放置包含多个 receiver 参数的参数列表,或者变长 receiver 参数。
      • 方法接收器(receiver)参数、函数 / 方法参数,以及返回值变量对应的作用域范围,都是函数 / 方法体对应的显式代码块。
      • receiver 部分的参数名不能与方法参数列表中的形参名,以及具名返回值中的变量名存在冲突,必须在这个方法的作用域中具有唯一性。
      • 除了 receiver 参数名字要保证唯一外,Go 语言对 receiver 参数的基类型也有约束,那就是 receiver 参数的基类型本身不能为指针类型或接口类型。
      • Go 要求,方法声明要与 receiver 参数的基类型声明放在同一个包内。
        • 我们不能为原生类型(诸如 int、float64、map 等)添加方法。
        • 不能跨越 Go 包为其他包的类型声明新方法。
    • Go 语言中的方法的本质就是,一个以方法的 receiver 参数作为第一个参数的普通函数。

方法集合与如何选择 receiver 类型

receiver 参数类型对 Go 方法的影响

  • 我们直接来看下面例子中的两个 Go 方法,以及它们等价转换后的函数:
    func (t T) M1() <=> F1(t T)
    func (t *T) M2() <=> F2(t *T)
    
  • 首先,当 receiver 参数的类型为 T 时:
    • 当我们选择以 T 作为 receiver 参数类型时,M1 方法等价转换为F1(t T)。
    • Go 函数的参数采用的是值拷贝传递,也就是说,F1 函数体中的 t 是 T 类型实例的一个副本。
    • 这样,我们在 F1 函数的实现中对参数 t 做任何修改,都只会影响副本,而不会影响到原 T 类型实例。
    • 当我们的方法 M1 采用类型为 T 的 receiver 参数时,代表 T 类型实例的 receiver 参数以值传递方式传递到 M1 方法体中的,实际上是 T 类型实例的副本,M1 方法体中对副本的任何修改操作,都不会影响到原 T 类型实例。
  • 第二,当 receiver 参数的类型为 *T 时:
    • 当我们选择以 *T 作为 receiver 参数类型时,M2 方法等价转换为F2(t *T)。
    • 我们传递给 F2 函数的 t 是 T 类型实例的地址,这样 F2 函数体中对参数 t 做的任何修改,都会反映到原 T 类型实例上。
    • 当我们的方法 M2 采用类型为 *T 的 receiver 参数时,代表 *T 类型实例的 receiver 参数以值传递方式传递到 M2 方法体中的,实际上是 T 类型实例的地址,M2 方法体通过该地址可以对原 T 类型实例进行任何修改操作。

选择 receiver 参数类型的原则

  • 第一个原则
    • *如果 Go 方法要把对 receiver 参数代表的类型实例的修改,反映到原类型实例上,那么我们应该选择 T 作为 receiver 参数的类型。
    • 无论是 T 类型实例,还是 *T 类型实例,都既可以调用 receiver 为 T 类型的方法,也可以调用 receiver 为 *T 类型的方法。
  • 第二个原则
    • 如果我们不需要在方法中对类型实例进行修改呢?这个时候我们是为 receiver 参数选择 T 类型还是 *T 类型呢?
    • 一般情况下,我们通常会为 receiver 参数选择 T 类型,因为这样可以缩窄外部修改类型实例内部状态的“接触面”,也就是尽量少暴露可以修改类型内部状态的方法。
    • *考虑到 Go 方法调用时,receiver 参数是以值拷贝的形式传入方法中的。那么,如果 receiver 参数类型的 size 较大,以值拷贝形式传入就会导致较大的性能开销,这时我们选择 T 作为 receiver 类型可能更好些。
  • 第三个原则
    • 方法集合是用来判断一个类型是否实现了某接口类型的唯一手段,可以说,“方法集合决定了接口实现”。
      • Go 中任何一个类型都有属于自己的方法集合,或者说方法集合是 Go 类型的一个“属性”。但不是所有类型都有自己的方法,比如 int 类型就没有。所以,对于没有定义方法的 Go 类型,我们称其拥有空方法集合。
      • 接口类型相对特殊,它只会列出代表接口的方法列表,不会具体定义某个方法,它的方法集合就是它的方法列表中的所有方法,我们可以一目了然地看到。
      • Go 语言规定,*T 类型的方法集合包含所有以 *T 为 receiver 参数类型的方法,以及所有以 T 为 receiver 参数类型的方法。
      • 如果某类型 T 的方法集合与某接口类型的方法集合相同,或者类型 T 的方法集合是接口类型 I 方法集合的超集,那么我们就说这个类型 T 实现了接口 I。
    • 如果 T 类型需要实现某个接口,那我们就要使用 T 作为 receiver 参数的类型,来满足接口类型方法集合中的所有方法。
    • 如果 T 不需要实现某一接口,但 *T 需要实现该接口,那么根据方法集合概念,*T 的方法集合是包含 T 的方法集合的,这样我们在确定 Go 方法的 receiver 的类型时,参考原则一和原则二就可以了。

如何用类型嵌入模拟实现“继承”

什么是类型嵌入

  • 类型嵌入指的就是在一个类型的定义中嵌入了其他类型。Go 语言支持两种类型嵌入,分别是接口类型的类型嵌入和结构体类型的类型嵌入。
  • 接口类型的类型嵌入
    • 接口类型声明了由一个方法集合代表的接口,比如下面接口类型 E:
      type E interface {
      	M1()
      	M2()
      }
      
      • 这个接口类型 E 的方法集合,包含两个方法,分别是 M1 和 M2,它们组成了 E 这个接口类型所代表的接口。
      • 如果某个类型实现了方法 M1 和 M2,我们就说这个类型实现了 E 所代表的接口。
      • 此时,我们再定义另外一个接口类型 I,它的方法集合中包含了三个方法 M1、M2 和 M3,如下面代码:
        type I interface {
        	M1()
        	M2()
        	M3()
        }
        
      • 接口类型 I 方法集合中的 M1 和 M2,与接口类型 E 的方法集合中的方法完全相同。在这种情况下,我们可以用接口类型 E 替代上面接口类型 I 定义中 M1 和 M2:
        type I interface {
        	EM
        	3()
        }
        
    • 在一个接口类型(I)定义中,嵌入另外一个接口类型(E)的方式,就是接口类型的类型嵌入。
    • 接口类型嵌入的语义就是新接口类型(如接口类型 I)将嵌入的接口类型(如接口类型 E)的方法集合,并入到自己的方法集合中。
  • 结构体类型的类型嵌入
    • Go 结构体类型定义有另外一种形式,那就是带有嵌入字段(Embedded Field)的结构体定义。
      type T1 int
      type t2 struct{
      	n int
      	m int
      } 
      type I interface {
      	M1()
      } 
      type S1 struct {
      	T1
      	*t2
      	Ia
      	int
      	b string
      }
      
    • 这种以某个类型名、类型的指针类型名或接口类型名,直接作为结构体字段的方式就叫做结构体的类型嵌入,这些字段也被叫做嵌入字段(Embedded Field)。
    • 如果嵌入类型的名字是首字母大写的,那么也就说明这个嵌入字段是可导出的。

类型嵌入与方法集合

  • 嵌入类型的方法集合并入到新接口类型的方法集合中,并且,接口类型只能嵌入接口类型。而结构体类型对嵌入类型的要求就比较宽泛了,可以是任意自定义类型或接口类型。
  • 结构体类型的方法集合,包含嵌入的接口类型的方法集合。
  • 嵌入了其他类型的结构体类型本身是一个代理,在调用其实例所代理的方法时,Go 会首先查看结构体自身是否实现了该方法。
    • 如果实现了,Go 就会优先使用结构体自己实现的方法。
    • 如果没有实现,那么 Go 就会查找结构体中的嵌入字段的方法集合中,是否包含了这个方法。
    • 如果多个嵌入字段的方法集合中都包含这个方法,那么我们就说方法集合存在交集。
    • 这个时候,Go 编译器就会因无法确定究竟使用哪个方法而报错。
  • 结构体类型嵌入接口类型在日常编码中有一个妙用,就是可以简化单元测试的编写。
    • 由于嵌入某接口类型的结构体类型的方法集合包含了这个接口类型的方法集合,这就意味着,这个结构体类型也是它嵌入的接口类型的一个实现。
    • 即便结构体类型自身并没有实现这个接口类型的任意一个方法,也没有关系。
  • 在结构体类型中嵌入结构体类型,为 Gopher 们提供了一种“实现继承”的手段,外部的结构体类型 T 可以“继承”嵌入的结构体类型的所有方法的实现。并且,无论是 T 类型的变量实例还是 *T 类型变量实例,都可以调用所有“继承”的方法。
  • Go 语言中,凡通过类型声明语法声明的类型都被称为 defined 类型。
    • 对于那些基于接口类型创建的 defined 的接口类型,它们的方法集合与原接口类型的方法集合是一致的。
    • 对于基于非接口类型的 defined 类型创建的非接口类型,基于自定义非接口类型的 defined 类型的方法集合为空的事实,也决定了即便原类型实现了某些接口,基于其创建的 defined 类型也没有“继承”这一隐式关联。也就是说,新 defined 类型要想实现那些接口,仍然需要重新实现接口的所有方法。
  • 无论原类型是接口类型还是非接口类型,类型别名都与原类型拥有完全相同的方法集合。

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

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

相关文章

高忆管理:培育钻石价格大跌,力量钻石等多家概念股业绩下滑

8月29日&#xff0c;培养钻石板块直线拉升&#xff0c;其间沃尔德&#xff08;688028.SH&#xff09;涨幅达10.89%&#xff0c;力气钻石(301071.SZ)、惠丰钻石&#xff08;839725.BJ&#xff09;、四方达&#xff08;300179.SZ&#xff09;、黄河旋风&#xff08;600172.SH&…

【STM32】学习笔记(TIM定时器)-江科大

TIM&#xff08;Timer&#xff09;定时器 定时器可以对输入的时钟进行计数&#xff0c;并在计数值达到设定值时触发中断 16位计数器、预分频器、自动重装寄存器的时基单元&#xff0c;在72MHz计数时钟下可以实现最大59.65s的定时 不仅具备基本的定时中断功能&#xff0c;而且…

SpringBoot实现简单的登录验证码

参考了一些资料&#xff0c;完成了这个验证码的功能&#xff0c;下面记录一下功能的实现过程。 一、效果图 二、实现原理 后台生成验证码图片&#xff0c;将图片传到前台。后台在session中保存验证码内容。前台输入验证码后传到后台在后台取出session中保存的验证码进行校验。…

x86架构 指令INT3只有一个字节的原因

文章目录 一、单字节原因简介二、断点原理三、单字节具体原因参考资料 一、单字节原因简介 INT3指令生成一个特殊的单字节操作码&#xff08;CC&#xff09;&#xff0c;用于调用调试异常处理程序。&#xff08;这种单字节形式很有价值&#xff0c;因为它可以用来用断点替换任何…

冠达管理:火爆!拼多多飙涨15%,中概股沸腾!这些外资巨头唱多中国资产

当地时间8月29日&#xff0c;美国三大股指团体收涨&#xff0c;道指涨0.85%&#xff0c;标普500指数涨1.45%&#xff0c;纳指涨1.74%。科技股大涨&#xff0c;特斯拉涨7.69%&#xff0c;英伟达涨4.16%。纳斯达克我国金龙指数收涨3.7%&#xff0c;拼多多涨超15%。 广东研山私募…

开源百度电商小程序源码 含完整代码包+安装部署教程 一键搭建商城小程序

分享一款开源百度电商小程序源码&#xff0c;含完整代码包安装部署教程&#xff0c;一键搭建商城小程序&#xff0c;源码开源可二开&#xff0c;已测试完美运营版&#xff0c;帮你一键搭建百度商城小程序&#xff0c;含多套模板、自由DIY功能和完整的搭建部署教程。程序支持除百…

微信测试号实现微信分享等功能

目录 1 申请微信测试号 2 测试号信息 3 下载微信的测试代码 4 将下载的代码放到自己的服务器上 5 接口配置信息 6 JS安全域名 ​7 扫码关注&#xff0c;只有关注的微信号才能测试 ​8 修改sample.php文件 9 在微信上打开&#xff0c;并分享 10 问题 1 申请微信测试号 …

【USRP】调制解调系列1:AM、FM解调

AM&#xff0c;DSB&#xff0c;SSB和VSB的联系是都为幅度调制&#xff08;调幅&#xff09; 区别&#xff1a; AM是调幅&#xff0c;带载波。 DSB是抑制载波的调幅&#xff0c;可增加功率效率&#xff0c;但两个边带均传输相同的信息。 SSB单边带抑制了一个边带&#xff0c…

高忆管理:etf联接基金有哪些?

ETF联接基金自面世以来&#xff0c;就备受出资者重视&#xff0c;由于它可以以比较低的本钱获得和国际市场相同的收益&#xff0c;而且具备杰出的流动性和简略易懂的买卖方法。那么&#xff0c;ETF联接基金都有哪些呢&#xff1f; 一、什么是ETF联接基金&#xff1f; ETF联接基…

Vue怎么安装?看这一篇就够了!

Vue2.0 安装 一般我们都不会单独用 npm 去安装 Vue 插件&#xff0c;而是通过脚手架 Vue CLI 去初始化一个 Vue 项目。 Vue CLI 安装 ::: warning 注意 使用默认的镜像源安装 npm 第三方包可能要很长时间&#xff0c;建议你已经替换了新的镜像源。如需要请查看 npm 镜像源修…

不用订阅,不用破解,永久免费使用Axure最新版教程

首先去官网下载最新的axure&#xff0c;你没听错&#xff0c;就是最新的。 下载网址&#xff1a;Axure RP - UX Prototypes, Specifications, and Diagrams in One Tool 下载完后解压安装到本地&#xff0c;并注册属于你自己的账户&#xff0c;开始试用。可惜的是只有30天的试…

零基础如何备考通过PMP?

​01为什么要去考&#xff1f;新手能考吗&#xff1f; 首先PO一个官方解释&#xff1a; PMP——项目管理专业人士资格认证&#xff0c;由项目管理协会&#xff08;Project Management Institute&#xff09;发起&#xff0c;在全球206多个国家和地区得到高度认可。 通俗点说就…

如何自己实现一个丝滑的流程图绘制工具(七)bpmn-js 批量删除、复制节点

背景 希望实现批量删除和复制节点&#xff0c;因为bpmn-js是canvas画的&#xff0c;所以不能像平时页面上的复制一样直接选择范围&#xff0c;会变成移动画布。 思路是&#xff1a; 绘制一个选择的效果框&#xff0c;这样才可以看出来选的节点有哪些。 上面的选中范围框效果…

CSS 实现平面圆点绕椭圆动画

前言 &#x1f44f;CSS实现平面圆点绕椭圆动画,速速来Get吧~ &#x1f947;文末分享源代码。记得点赞关注收藏&#xff01; 1.实现效果 2.实现原理 transform-style&#xff1a;CSS 属性 transform-style 设置元素的子元素是位于 3D 空间中还是平面中。如果选择平面&#xf…

Python GUI应用程序开发之wxPython使用详解

概要 wxPython是一个强大的跨平台GUI工具包&#xff0c;它使用Python编程语言开发&#xff0c;提供了丰富的控件功能。如果你是一名Python开发者&#xff0c;而且希望创建一个功能齐全的桌面应用程序&#xff0c;那么wxPython是一个值得考虑的选择。 什么是wxPython wxPython…

【论文阅读】你看不见我:对基于激光雷达的自动驾驶汽车驾驶框架的物理移除攻击

文章目录 AbstractIntroduction Abstract 自动驾驶汽车(AVs)越来越多地使用基于激光雷达的物体检测系统来感知道路上的其他车辆和行人。目前&#xff0c;针对基于激光雷达的自动驾驶架构的攻击主要集中在降低自动驾驶物体检测模型的置信度&#xff0c;以诱导障碍物误检测&…

宏观经济和风电预测误差分析(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

【LeetCode-中等题】199. 二叉树的右视图

文章目录 题目方法一&#xff1a;层序遍历取每一层最后一个元素方法二&#xff1a;深度优先搜索 题目 方法一&#xff1a;层序遍历取每一层最后一个元素 // 方法一 &#xff1a;层序 集合(取每层子集合最后一个元素)// List<List<Integer>> Rlist new ArrayList…

运动耳机哪款最好、热门运动耳机推荐

对于当代年轻人而言&#xff0c;运动健身已成为最流行的压力释放方式。然而&#xff0c;我们不得不承认&#xff0c;运动本身确实是一项沉闷而艰苦的挑战&#xff0c;需要我们付出大量汗水和持之以恒的坚持。 在运动过程中&#xff0c;许多人会借助音乐的律动来激励自己不断向前…

「2024」预备研究生mem-数学进阶卷(二)8.27 比较易错的难题,存在特殊方法

一共12道题&#xff1a; 回忆&#xff1a; 不错&#xff1a; 男生 没有要求顺序 &#xff0c;就不用A55了