当前位置: 首页 > news >正文

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

方法

认识 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 {EM3()
        }
        
    • 在一个接口类型(I)定义中,嵌入另外一个接口类型(E)的方式,就是接口类型的类型嵌入。
    • 接口类型嵌入的语义就是新接口类型(如接口类型 I)将嵌入的接口类型(如接口类型 E)的方法集合,并入到自己的方法集合中。
  • 结构体类型的类型嵌入
    • Go 结构体类型定义有另外一种形式,那就是带有嵌入字段(Embedded Field)的结构体定义。
      type T1 int
      type t2 struct{n intm int
      } 
      type I interface {M1()
      } 
      type S1 struct {T1*t2Iaintb string
      }
      
    • 这种以某个类型名、类型的指针类型名或接口类型名,直接作为结构体字段的方式就叫做结构体的类型嵌入,这些字段也被叫做嵌入字段(Embedded Field)。
    • 如果嵌入类型的名字是首字母大写的,那么也就说明这个嵌入字段是可导出的。

类型嵌入与方法集合

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

相关文章:

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

方法 认识 Go 方法 Go 语言从设计伊始&#xff0c;就不支持经典的面向对象语法元素&#xff0c;比如类、对象、继承&#xff0c;等等&#xff0c;但 Go 语言仍保留了名为“方法&#xff08;method&#xff09;”的语法元素。当然&#xff0c;Go 语言中的方法和面向对象中的方…...

基于RUM高效治理网站用户体验入门-价值篇

用户体验 用户体验基本包含访问网站的性能、可用性和正确性。通俗的讲&#xff0c;就是一把通过用户访问测量【设计者】意图的尺子。 本文目的 网站如何传递出设计者的意图&#xff0c;可能页面加载时间太长、或者页面在用户的浏览器中渲染时间太慢&#xff0c;或者第三方设备…...

Unity之Photon PUN2开发多人游戏如何实现组队功能

前言 Photon Unity Networking 2 (PUN2) 是一款基于Photon Cloud的Unity多人游戏开发框架。它提供了一系列易于使用的API和工具,使开发者可以快速构建多人戏,并轻松处理多人游戏中的网络同步、房间管理、玩家匹配等问题。 我们在查看Pun2的Demo时,会发现Demo中自带了一个简…...

大数据Flink简介与架构剖析并搭建基础运行环境

文章目录 前言Flink 简介Flink 集群剖析Flink应用场景Flink基础运行环境搭建Docker安装docker-compose文件编写创建并运行容器访问Flink web界面 前言 前面我们分别介绍了大数据计算框架Hadoop与Spark,虽然他们有的有着良好的分布式文件系统和分布式计算引擎&#xff0c;有的有…...

RISC-V IOPMP实际用例-Rapid-k模型在NVIDIA上的应用

安全之安全(security)博客目录导读 2023 RISC-V中国峰会 安全相关议题汇总 说明&#xff1a;本文参考RISC-V 2023中国峰会如下议题&#xff0c;版权归原作者所有。...

【UE5】给模型指定面添加自定义材质

实现步骤 1. 首先我们向UE中导入一个简单的模型&#xff0c;可以看到目前该模型的材质插槽只有一个&#xff0c;当我们修改材质时会使得模型整体的材质全部改变&#xff0c;如果我们只想改变模型的某些面的材质就需要继续做后续操作。 2. 选择建模模式 3. 在模式工具栏中点击…...

mall:redis项目源码解析

文章目录 一、mall开源项目1.1 来源1.2 项目转移1.3 项目克隆 二、Redis 非关系型数据库2.1 Redis简介2.2 分布式后端项目的使用流程2.3 分布式后端项目的使用场景2.4 常见的缓存问题 三、源码解析3.1 集成与配置3.1.1 导入依赖3.1.2 添加配置3.1.3 全局跨域配置 3.2 Redis测试…...

RISC-V Linux系统kernel制作

文章目录 1、下载2、编译 1、下载 Linux 官网地址:https://www.kernel.org $ wget https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-5.10.181.tar.xz $ tar xvf linux-5.10.181.tar.xz $ cd linux-5.10.1812、编译 安装依赖 $ sudo apt-get install -y flex bison bui…...

5G NR:PRACH时域资源

PRACH occasion时域位置由高层参数RACH-ConfigGeneric->prach-ConfigurationIndex指示&#xff0c;根据小区不同的频域和模式&#xff0c;38.211的第6.3.3节中给出了prach-ConfigurationIndex所对应的表格。 小区频段为FR1&#xff0c;FDD模式(paired频谱)/SUL&#xff0c;…...

LLaMA-2的模型架构

输入token&#xff1b;[B, L] 输出probs:[B, L, vab_size]...

掌握Java框架之Struts,开启高效开发之旅!

当今的软件开发世界&#xff0c;Java框架如Struts已经成为构建企业级应用的重要工具。Struts作为一个流行的MVC框架&#xff0c;不仅简化了Java Web开发&#xff0c;还提高了软件的可维护性和可扩展性。本文将带你走进Struts的世界&#xff0c;探索其魅力所在&#xff0c;让你领…...

关于Vue.set()

简介 Vue.set() 是 Vue 中的一个全局方法&#xff0c;其主要作用是向响应式对象添加新的属性&#xff0c;并确保新属性同样具有响应式。在 Vue.js 中&#xff0c;当数据对象的属性被直接修改时&#xff0c;Vue 可以监测到数据变化并响应变化。但若添加新的响应式对象属性时&am…...

Selenium 遇见伪元素该如何处理?

问题发生 在很多前端页面中&#xff0c;大家会见到很多&#xff1a;:before、::after 元素&#xff0c;比如【百度流量研究院】&#xff1a; 比如【百度疫情大数据平台】&#xff1a; 以【百度疫情大数据平台】为例&#xff0c;“累计确诊”文本并没有显示在 HTML 源代码中&am…...

RPA技术介绍与应用价值

一、什么是RPA技术? RPA(Robotic Process Automation)机器人流程自动化,是一种能够模拟人类来执行重复性任务的新型技术。RPA可实现统筹安排、自动化业务处理,并提升业务工作流处理效率。用户只需通过图形方式显示的计算机操作界面对RPA软件进行动态设定即可。借助RPA (R…...

产品经理,需要具备哪些能力和知识

作为产品经理&#xff0c;需要具备以下能力和知识&#xff1a; 产品管理能力&#xff1a;具备全面的产品管理能力&#xff0c;包括产品策划、需求分析、产品规划、产品设计、项目管理、市场调研和竞争分析等。 用户导向思维&#xff1a;能够理解用户需求和期望&#xff0c;以…...

【C++】map和set

map和set 文章目录 map和set关联式容器setset介绍set的函数测试代码 multiset注意事项测试代码 mapmap介绍map的函数测试代码 关联式容器 前面了解过的vector&#xff0c;list&#xff0c;string等容器都是序列式容器&#xff0c;存储的都是元素本身&#xff0c;底层都是线性的…...

crawlab通过docker单节点部署简单爬虫

crawlab 单节点docker安装 此处介绍的是单节点的方式&#xff0c;多节点的情况可以把爬虫上传到一个节点中&#xff0c;之后会同步到其它节点上 version: 3.3 services:master:image: crawlabteam/crawlabcontainer_name: crawlab_masterrestart: alwaysenvironment:CRAWLAB…...

【STM32】中断与NVIC以外部中断为例

前言 在stm32中姑且可以认为&#xff0c;异常就是中断 单片机上电之后&#xff0c;首先执行启动文件&#xff0c;开辟堆栈之后&#xff0c;开始初始化中断向量表。 NVIC NVIC NVIC是嵌套向量中断控制器&#xff0c;控制着整个芯片中断相关的功能&#xff0c;它跟内核紧密耦…...

大学生网页设计制作作业实例代码 (全网最全,建议收藏) HTML+CSS+JS

文章目录 &#x1f4da;web前端期末大作业 (1500套) 集合一、网页介绍二、网页集合 三、作品演示A电影主题B漫画主题C商城主题D家乡主题E旅游主题F餐饮/美食主题G环境主题H游戏主题I 个人主题K体育主题L博客主题M汽车主题N文化主题P美妆主题Q企业主题R教育主题S其他主题 &#…...

Llama模型结构解析(源码阅读)

目录 1. LlamaModel整体结构流程图2. LlamaRMSNorm3. LlamaMLP4. LlamaRotaryEmbedding 参考资料&#xff1a; https://zhuanlan.zhihu.com/p/636784644 https://spaces.ac.cn/archives/8265 ——《Transformer升级之路&#xff1a;2、博采众长的旋转式位置编码》 前言&#x…...

IDEA运行Tomcat出现乱码问题解决汇总

最近正值期末周&#xff0c;有很多同学在写期末Java web作业时&#xff0c;运行tomcat出现乱码问题&#xff0c;经过多次解决与研究&#xff0c;我做了如下整理&#xff1a; 原因&#xff1a; IDEA本身编码与tomcat的编码与Windows编码不同导致&#xff0c;Windows 系统控制台…...

模型参数、模型存储精度、参数与显存

模型参数量衡量单位 M&#xff1a;百万&#xff08;Million&#xff09; B&#xff1a;十亿&#xff08;Billion&#xff09; 1 B 1000 M 1B 1000M 1B1000M 参数存储精度 模型参数是固定的&#xff0c;但是一个参数所表示多少字节不一定&#xff0c;需要看这个参数以什么…...

uni-app学习笔记二十二---使用vite.config.js全局导入常用依赖

在前面的练习中&#xff0c;每个页面需要使用ref&#xff0c;onShow等生命周期钩子函数时都需要像下面这样导入 import {onMounted, ref} from "vue" 如果不想每个页面都导入&#xff0c;需要使用node.js命令npm安装unplugin-auto-import npm install unplugin-au…...

基础测试工具使用经验

背景 vtune&#xff0c;perf, nsight system等基础测试工具&#xff0c;都是用过的&#xff0c;但是没有记录&#xff0c;都逐渐忘了。所以写这篇博客总结记录一下&#xff0c;只要以后发现新的用法&#xff0c;就记得来编辑补充一下 perf 比较基础的用法&#xff1a; 先改这…...

搭建DNS域名解析服务器(正向解析资源文件)

正向解析资源文件 1&#xff09;准备工作 服务端及客户端都关闭安全软件 [rootlocalhost ~]# systemctl stop firewalld [rootlocalhost ~]# setenforce 0 2&#xff09;服务端安装软件&#xff1a;bind 1.配置yum源 [rootlocalhost ~]# cat /etc/yum.repos.d/base.repo [Base…...

第7篇:中间件全链路监控与 SQL 性能分析实践

7.1 章节导读 在构建数据库中间件的过程中&#xff0c;可观测性 和 性能分析 是保障系统稳定性与可维护性的核心能力。 特别是在复杂分布式场景中&#xff0c;必须做到&#xff1a; &#x1f50d; 追踪每一条 SQL 的生命周期&#xff08;从入口到数据库执行&#xff09;&#…...

flow_controllers

关键点&#xff1a; 流控制器类型&#xff1a; 同步&#xff08;Sync&#xff09;&#xff1a;发布操作会阻塞&#xff0c;直到数据被确认发送。异步&#xff08;Async&#xff09;&#xff1a;发布操作非阻塞&#xff0c;数据发送由后台线程处理。纯同步&#xff08;PureSync…...

解析“道作为序位生成器”的核心原理

解析“道作为序位生成器”的核心原理 以下完整展开道函数的零点调控机制&#xff0c;重点解析"道作为序位生成器"的核心原理与实现框架&#xff1a; 一、道函数的零点调控机制 1. 道作为序位生成器 道在认知坐标系$(x_{\text{物}}, y_{\text{意}}, z_{\text{文}}…...

跨平台商品数据接口的标准化与规范化发展路径:淘宝京东拼多多的最新实践

在电商行业蓬勃发展的当下&#xff0c;多平台运营已成为众多商家的必然选择。然而&#xff0c;不同电商平台在商品数据接口方面存在差异&#xff0c;导致商家在跨平台运营时面临诸多挑战&#xff0c;如数据对接困难、运营效率低下、用户体验不一致等。跨平台商品数据接口的标准…...

PostgreSQL 对 IPv6 的支持情况

PostgreSQL 对 IPv6 的支持情况 PostgreSQL 全面支持 IPv6 网络协议&#xff0c;包括连接、存储和操作 IPv6 地址。以下是详细说明&#xff1a; 一、网络连接支持 1. 监听 IPv6 连接 在 postgresql.conf 中配置&#xff1a; listen_addresses 0.0.0.0,:: # 监听所有IPv4…...