【新人系列】Golang 入门(十五):类型断言

✍ 个人博客:https://blog.csdn.net/Newin2020?type=blog
📝 专栏地址:https://blog.csdn.net/newin2020/category_12898955.html
📣 专栏定位:为 0 基础刚入门 Golang 的小伙伴提供详细的讲解,也欢迎大佬们一起交流~
📚 专栏简介:在这个专栏,我将带着大家从 0 开始入门 Golang 的学习。在这个 Golang 的新人系列专栏下,将会总结 Golang 入门基础的一些知识点,并由浅入深的学习这些知识点,方便大家快速入门学习~
❤️ 如果有收获的话,欢迎点赞 👍 收藏 📁 关注,您的支持就是我创作的最大动力 💪

1. 快速了解

在 Go 语言(Golang)中,断言(Type Assertion)是一种用于在运行时检查接口值实际保存的具体类型,并获取对应类型值的机制。

例如,下面 add 函数中入参被定义成了空接口的类型,因此我们可以针对入参进行类型断言,通过 a.(int) 的方式来判断入参的类型是否为 int 类型。

func add(a, b interface{}) interface{} {ai, ok := a.(int)if !ok {panic("not int type")}bi, _ := b.(int)return ai + bi
}func main() {a := 1b := 2fmt.Println(add(a,b))
}

当然,我们可以利用 switch 语法来适应不同类型的断言:

func add(a, b interface{}) interface{} {switch a.(type) {case int :ai, _ := a.(int)bi, _ := b.(int)return ai + bicase int32:ai, _ := a.(int32)bi, _ := b.(int32)return ai + bicase float64:af, _ := a.(float64)bf, _ := b.(float64)return af + bfdefault:panic("not supported type")}
}func main() {fmt.Println(add(1,2))fmt.Println(add(1.2,2.3))
}

而从前面的接口篇章可以得知,接口可以分为「空接口」和「非空接口」两类。
相对于接口这种「抽象类型」,int、string、slice 等等都可以被称为「具体类型」。

在这里插入图片描述

  • 类型断言作用于接口值之上,可以是空接口或非空接口
  • 断言的目标类型,可以是具体类型或非空接口类型

这样,就组合出了四种类型断言,接下来我们就逐一看看它们究竟是怎样 “断言” 的。

在这里插入图片描述

2 空接口.(具体类型)

var e interface {}r, ok := e.(*os.File)

上面的代码中 e.(*os.File) 是要判断 e 的动态类型是否为 *os.File,而这只需要确定 _type 是否指向 *os.File 的类型元数据即可。

在这里插入图片描述

在 go 语言里每种类型的类型元数据都是全局唯一的,如果像下面这样给 e 赋值,那么 e 的动态值就是 f,动态类型就是 *os.File。

所以下面代码会断言成功,ok 为 true,r 被赋值为 e 的动态值。

var e interface {}f, _ := os.Open("output.txt")
e = fr, ok := e.(*os.File)

在这里插入图片描述

而如果像下面这样赋值,e 的动态类型就是 string 类型,那么类型断言就会失败。

此时 ok 就为 false,r 会被赋值为 *os.File 的类型零值 nil。

var e interface {}f  := "output"
e = fr, ok := e.(*os.File)

在这里插入图片描述

3 非空接口.(具体类型)

var rw io.ReadWriterr, ok := rw.(*os.File)

上面代码中 rw.(*os.File) 是要判断 rw 的动态类型是否为 *os.File。

在这里插入图片描述

之前的篇章中,我们也介绍过,程序中用到的 itab 结构体都会缓存起来,可以通过接口类型和动态类型组合起来的 key,查找到对应的 itab 指针。

所以这里的类型断言只需要一次比较就能完成,只要看 tab = &itab 是否指向这个 itab 结构体即可

在这里插入图片描述

如果 rw 像下面代码这样赋值,那么它的动态值就是 f,动态类型就是 *os.File。

var rw io.ReadWriterf, _ := os.Open("output.txt")
rw = fr, ok := rw.(*os.File)

所以 tab 指向下面图中第二块部分的这个 itab 结构体,因此类型断言成功,ok 为 true 且 r 被赋值为 rw 的动态值。

在这里插入图片描述

然而,如果 rw 动态类型不是 *os.File。

var rw io.ReadWriterf, _ := output{name: "output"}
rw = fr, ok := rw.(*os.File)

那么此时 tab 就指向下面图中第一块部分,而不是图中第二块部分的地方。所以类型断言就会失败,ok 为 false 且 r 会被置为 *os.File 的类型零值 nil。

在这里插入图片描述

4. 空接口.(非空接口)

var e interface {}rw, ok := e.(ioReadWriter)

e.(ioReadWriter) 是要判断 e 的动态类型是否实现了 io.ReadWriter 接口,当然之前的篇章中我们已经介绍过类型关联的方法列表该去哪里找了。

在这里插入图片描述

如果像下面代码中 e 这样赋值,那么它的动态值就是 f,动态类型就是 *os.File。

var e interface {}f, _ := os.Open("output.txt")
e = frw, ok := e.(ioReadWriter)

虽然 *os.File 元数据后面可以找到类型关联的方法元数据数组,但也不必每次都去检查这里是否有对应接口要求的所有方法。

在这里插入图片描述

因为存在 itab 缓存,所以可以先去 itab 缓存中查找一下。若没有 io.ReadWriter 和 *os.File 对应的 itab 结构体,再去检查 *os.File 的方法列表也不迟。

在这里插入图片描述

值得强调的是,就算能够从缓存中查找到对应的 itab 指针,也要进一步判断 itab.fun[0] 是否等于 0。

在这里插入图片描述

这是因为断言失败的类型组合,其对应的 itab 结构体也会被缓存起来,只是会把 itab.fun[0] 置为 0,用以表示这里的动态类型并没有实现对应的接口。

这样以后再遇到同种类型断言时,就不用再去检查方法列表了,可以直接断言失败。

在这里插入图片描述

而我们上面这个例子中,类型断言是成功的,所以 ok 为 true,而 rw 就是一个 io.ReadWriter 类型的变量,其动态值与 e 相同,tab 就指向下面这样一个 itab 结构体。

在这里插入图片描述

但如果 e 被赋值为一个字符串,那么它的动态类型就是 string,并没有实现要求的 Read 和 Write 方法,所以对应的 itab 中 fun[0] 的值为 0,并且会被添加到 itab 缓存中。

var e interface {}f, _ := "output"
e = frw, ok := e.(ioReadWriter)

在这里插入图片描述

因此这里断言失败,ok 为 false 且 rw 为 io.ReadWriter 的类型零值 nil。

5. 非空接口.(非空接口)

var w io.Writerrw, ok := w.(io.ReadWriter)

w.(io.ReadWriter) 是要判断 w 存储的动态类型是否实现了 io.ReadWriter 接口,w 是 io.Writer 类型,接口要求一个 Write 方法,而 io.ReadWriter 接口要求实现 Read 和 Write 两个方法。

在这里插入图片描述

如果 w 像下面的代码这样赋值,其动态值就是 f。

var w io.Writerf, _ := os.Open("output.txt")
w = frw, ok := w.(io.ReadWriter)

tab 指向下图这样一个 itab 结构体,要确定 *os.File 是否实现了 io.ReadWriter 接口,同样会先去 itab 缓存里查找这个组合对应的 itab 指针。

在这里插入图片描述

若存在,且 itabfun[0] 不等于 0,则断言成功;若不存在,再去检查 *os.File 的方法列表,并缓存 itab 信息。

而在这里的代码中,断言是成功的,即 ok 为 true 且 rw 为 io.ReadWriter 类型的变量,动态值与 w 相同,而 tab 指向下面这个 itab 结构体。

在这里插入图片描述

如果我们自定义一个 output 类型,并且 *output 类型只实现了 io.Writer 接口,并没有实现 io.ReadWriter 接口。

type output struct {name string
}func (o *output) Write(b []byte) (n int, err error) {return len(o.name), nil
}

现在如果把这个 output 类型的变量赋值给 w,那么此时 w 的动态类型就为 *output,并没有实现指定接口,所以 fun[0] = 0,该 itab 结构体就会被缓存起来。

var w io.Writerf, _ := output{name: "john")
w = &frw, ok := w.(io.ReadWriter)

因此,类型断言会失败,ok 为 false 且 rw 的 tab 和 data 均为 nil。

所以,类型断言的关键是明确接口的动态类型,以及对应的类型实现了哪些方法。而明确这些的关键还是「类型元数据」,以及空接口与非空接口的「数据结构」。

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

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

相关文章

AI大模型发展现状与MCP协议诞生的技术演进

1. 大模型能力边界与用户痛点(2023年) 代表模型:GPT-4(OpenAI)、Claude 3(Anthropic)、通义千问(阿里云)等展现出强大的生成能力,但存在明显局限&#xff1a…

深入理解Linux中的线程控制:多线程编程的实战技巧

个人主页:chian-ocean 文章专栏-Linux 前言: POSIX线程(Pthreads) 是一种在 POSIX 标准下定义的线程库,它为多线程编程提供了统一的接口,主要用于 UNIX 和类 UNIX 系统(如 Linux、MacOS 和 BS…

(mac)Grafana监控系统之监控Linux的Redis

Grafana安装-CSDN博客 普罗米修斯Prometheus监控安装(mac)-CSDN博客 1.Redis_exporter安装 直接下载 wget https://github.com/oliver006/redis_exporter/releases/download/v1.0.3/redis_exporter-v1.0.3.linux-amd64.tar.gz 解压 tar -xvf redis_…

鸿蒙应用元服务开发-Account Kit未成年人模式订阅和处理用户信息变更

一、概述 通过订阅用户信息变更,您可以接收有关用户及其账户的重要更新。当用户取消元服务的授权信息、注销华为账号时,华为账号服务器会发送通知到元服务,元服务可以根据通知消息进行自身业务处理。 二、用户信息变更事件介绍 三、订阅用…

buildroot构建根文件系统报错(已解决大部分问题)

title: buildroot构建根文件系统报错(set FORCE_UNSAFE_CONFIGURE1) author: cbus categories: 小知识 tags:小知识 abbrlink: 53691 date: 2025-04-20 08:03:00 错误1 set FORCE_UNSAFE_CONFIGURE1 在使用buildroot构建根文件系统时,一切按照文档的配置&#xff0…

7.QT-常用控件-QWidget|font|toolTip|focusPolicy|styleSheet(C++)

font API说明font()获取当前widget的字体信息.返回QFont对象.setFont(const QFont& font)设置当前widget的字体信息. 属性说明family字体家族.⽐如"楷体",“宋体”,"微软雅⿊"等.pointSize字体⼤⼩weight字体粗细.以数值⽅式表⽰粗细程度取值范围为[…

通过面向目标的奖励弥合人与机器人的灵活性差距

24年10月来自纽约大学的论文“Bridging the Human to Robot Dexterity Gap through Object-Oriented Rewards”。 直接通过人类视频训练机器人是机器人技术和计算机视觉领域的一个新兴领域。尽管双指机械手在双指夹持器方面取得了显著进展,但以这种方式让多指机械手…

C++入门篇(下)

目录 1、引用 1.1 引用概念 1.2 引用特性 1.3 常引用 1.4 使用场景 1.4.1 引用做参数 1.4.2 引用做返回值 1.5 引用和指针的区别 2、内联函数 2.1 概念 2.2 特性 3、auto关键字 4、基于范围的for循环 5、指针空值nullptr 5.1 C98 中的指针空值处理 5.2 C11 …

Multi-Query Attention (MQA) PyTorch 实现

和多头注意力机制的唯一区别:K、V在不同的head之间实现了复用,而对于不同的头,Q依然不同。 因此这里的代码和标准多头注意力的实现也是几乎完全一样: import torch import torch.nn as nn import torch.nn.functional as Fclass…

visual studio无法跳转到函数定义、变量定义、跳转函数位置不准问题解决

参考:https://blog.csdn.net/snakehacker/article/details/135438353 程序有时会出现大部分函数都不能准确的从头文件中正确定位到函数定位,这是因为数据库错乱造成的,可以通过重构数据库来解决,操作方法如下: 菜单栏:工具——选项 文本编辑…

Java优雅实现判空方法

在 Java 开发中,频繁的 if (obj ! null) 判空代码会导致代码冗余、可读性差,且容易遗漏判空导致 NullPointerException。以下从 语言特性、设计模式、工具类 和 编码规范 四个维度,结合实际案例,详解如何优雅处理空值问题。 一、…

京东百亿补贴杀入外卖市场:一场关乎即时零售未来的攻防战

当美团和饿了么在外卖市场双雄争霸十余年之际,京东突然以"百亿补贴免佣金"的组合拳高调入场。这场看似跨界的外卖大战,实则是互联网巨头对万亿级即时零售市场的生死争夺。 外卖只是表象,即时零售才是终极战场 京东黑板报4月10日官…

UNION和UNION ALL的主要区别

UNION和UNION ALL的主要区别在于处理重复数据和排序的方式。 UNION和UNION ALL都是SQL语言中用于合并两个或多个SELECT语句结果集的关键字。它们的主要区别如下: 1、对重复结果的处理:UNION在进行表链接后会筛选掉重复的记录,而UNION ALL不会…

七段码 路径压缩 并查集 dfs

12.七段码 - 蓝桥云课 将七个二极管映射为 1-7 开一个二维矩阵 为 相邻的边连上线 edge[1][2] edge[1][6] 1;edge[2][1] edge[2][3] edge[2][7] 1;edge[3][2] edge[3][4] edge[3][7] 1;edge[4][3] edge[4][5] 1;edge[5][4] edge[5][6] edge[5][7] 1;edge[6][1…

科技如何改变世界?

技术是我们日常生活中不可或缺的一部分,以至于我们常常忘记了它的重要性。如果你正在科技领域工作,或者希望进入该领域,你可能是众多有使命感的人之一,希望知道自己的日常工作能为社会或地球的长远利益做出贡献。 别再四处寻找了…

抽象的https原理简介

前言 小明和小美是一对好朋友,他们分隔两地,平时经常写信沟通,但是偶然被小明发现他回给小美的信好像被人拆开看过,甚至偷偷被篡改过。 对称加密算法 开头的通信过程比较像HTTP服务器与客户端的通信过程,全明文传输…

高级java每日一道面试题-2025年4月13日-微服务篇[Nacos篇]-Nacos如何处理网络分区情况下的服务可用性问题?

如果有遗漏,评论区告诉我进行补充 面试官: Nacos如何处理网络分区情况下的服务可用性问题? 我回答: 在讨论 Nacos 如何处理网络分区情况下的服务可用性问题时,我们需要深入理解 CAP 理论以及 Nacos 在这方面的设计选择。Nacos 允许用户根据具体的应用…

python解压文件 zip tar.gz tar.xz

以下代码为解压zip包 tar包文件 zip_path:文件绝对路径 output_folder:文件解压后存放的文件夹路径 def extract_file(zip_path, output_folder):# 支持解压zip tar tar.gz tar.xz .tar.bz2# 确保输出文件夹存在os.makedirs(output_folder, exist_okT…

网络基础(协议,地址,OSI模型、Socket编程......)

目录 一、计算机网络发展 二、协议 1.认识协议 2.OSI七层模型 3.TCP/IP 五层(或四层)模型 4.协议本质 三、网络传输流程 1.MAC地址 2.协议栈 3.IP地址 IP地址 vs MAC地址 1. 核心区别 2. 具体通信过程类比 3. 关键总结 为什么需要两者? 4.协议栈图解…

生成式AI对话中提示词策略:明确问题、明确目标和提供背景信息是最有效的策略

生成式AI对话中提示词策略:明确问题、明确目标和提供背景信息是最有效的策略 最有效的提示词策略包括明确问题、明确目标和提供背景信息。普适性有效提示词策略可分为三类:明确需求与精确指引型、清晰解释与逻辑排序型、拆解任务与多样化表达型。[局限]数据来源于中国用户,…