什么是Go中的泛型与接口,它们都有哪些优缺点?

Golang 中的泛型与空接口

泛型简介

泛型允许在编写能够处理任意类型的代码,而无需在每次使用不同类型时都重新编写代码。泛型的核心是类型参数,这些参数在函数、结构体或接口中定义,并在使用时进行具体化。

泛型函数

使用泛型函数时,可以在函数名后面添加类型参数列表。类型参数列表使用方括号 [] 包裹,并且可以包含一个或多个类型参数。

示例:

// 泛型函数
func PrintSlice[T any](s []T) {for _, v := range s {fmt.Println(v)}
}

在这里,PrintSlice 是一个泛型函数,它接受一个类型为 T 的切片参数。T 是一个类型参数,可以是任何类型(any 是 Go 1.18 引入的一个特殊的类型约束,表示任何类型)。

泛型结构体

泛型也可以用于结构体定义中,这使得可以创建类型安全且通用的数据结构。

示例:

// 泛型结构体
type Pair[K, V any] struct {Key   KValue V
}

在这个示例中,Pair 是一个泛型结构体,它有两个类型参数 KV,分别表示键和值的类型。

常见用法

泛型函数的使用

泛型函数可以用于处理各种类型的数据,而无需重复代码。例如,一个通用的求和函数:

func Sum[T int | float64](a, b T) T {return a + b
}func main() {fmt.Println(Sum(1, 2))       // 输出: 3fmt.Println(Sum(1.5, 2.3))   // 输出: 3.8
}

在这个例子中,Sum 函数接受两个相同类型的参数 ab,它们可以是 intfloat64 类型。

泛型结构体的使用

泛型结构体可以用于创建灵活的数据结构,例如一个通用的键值对:

func main() {p1 := Pair[string, int]{Key: "age", Value: 30}p2 := Pair[int, string]{Key: 1, Value: "first"}fmt.Println(p1) // 输出: {age 30}fmt.Println(p2) // 输出: {1 first}
}

在这个例子中,Pair 结构体被具体化为不同的类型组合,从而实现了更高的灵活性。

使用约束

泛型可以使用约束来限制类型参数的范围,约束可以是接口或具体类型的组合。

示例:

type Number interface {int | int64 | float64
}func Add[T Number](a, b T) T {return a + b
}func main() {fmt.Println(Add(1, 2))         // 输出: 3fmt.Println(Add(int64(1), int64(2))) // 输出: 3fmt.Println(Add(1.1, 2.2))     // 输出: 3.3
}

在这个例子中,Number 是一个约束,限定了 Add 函数的类型参数必须是 intint64float64 类型。

类型约束的基本概念

类型约束(Type Constraints)是泛型的一部分,它用于限制泛型函数或泛型类型参数的范围。通过类型约束,可以指定类型参数必须满足某些条件,确保泛型代码的正确性和类型安全性。类型约束是定义在接口中的,可以是具体类型、接口或它们的组合。类型约束使用泛型接口来实现,确保类型参数符合特定的要求。

定义类型约束

类型约束通常定义在接口中。例如,你可以定义一个包含多个具体类型的接口:

type Number interface {int | int64 | float64
}

在这个示例中,Number 是一个类型约束接口,它表示可以是 intint64float64 类型的任意一种。

使用类型约束

在泛型函数或结构体中使用类型约束时,需要在类型参数列表中指定约束:

func Add[T Number](a, b T) T {return a + b
}

在这个示例中,Add 函数接受两个类型参数 ab,它们必须是满足 Number 约束的类型,即 intint64float64

常见类型约束用法

使用具体类型的类型约束

可以使用具体类型来限制类型参数的范围:

type MyConstraint interface {int | float64
}func Multiply[T MyConstraint](a, b T) T {return a * b
}

在这个例子中,MyConstraint 限制了 Multiply 函数的类型参数只能是 intfloat64 类型。

使用接口的类型约束

还可以使用接口作为类型约束,以确保类型参数实现了某些方法:

type Stringer interface {String() string
}func Print[T Stringer](s T) {fmt.Println(s.String())
}

在这个示例中,Print 函数的类型参数 T 必须实现 Stringer 接口,即必须有一个 String() 方法。

使用组合类型的类型约束

可以将具体类型和接口组合在一起作为类型约束:

type MyConstraint interface {int | float64 | Stringer
}func Process[T MyConstraint](value T) {// 处理代码
}

在这个例子中,MyConstraint 可以是 intfloat64 或实现了 Stringer 接口的类型。

泛型接口

泛型接口可以包含类型参数和类型约束,用于定义更加灵活和强大的接口:

type Adder[T any] interface {Add(a, b T) T
}type IntAdder struct{}func (IntAdder) Add(a, b int) int {return a + b
}func UseAdder[T any](adder Adder[T], a, b T) T {return adder.Add(a, b)
}func main() {intAdder := IntAdder{}result := UseAdder(intAdder, 3, 4)fmt.Println(result) // 输出: 7
}

在这个示例中,Adder 是一个泛型接口,IntAdder 实现了这个接口,并且 UseAdder 函数接受任何实现了 Adder 接口的类型。

以下,我们来讲解一下在Golang中的泛型与空接口的异同点。

空接口类型

定义和使用

空接口类型是Go语言中的一个特殊接口,它没有任何方法。因此,任何类型都实现了空接口,可以赋值给空接口类型的变量。

示例:

func Print(value interface{}) {fmt.Println(value)
}func main() {Print(42)Print("hello")Print(3.14)
}

在这个示例中,Print 函数接受一个空接口类型的参数,可以传递任何类型的值。

优点

  • 灵活性:空接口可以接受任何类型,因此非常灵活。
  • 简洁:代码简单,适用于处理多种类型的数据。

缺点

  • 类型安全性:空接口丧失了类型安全性,需要进行类型断言(type assertion)来恢复原始类型。
  • 性能开销:由于需要进行类型检查和类型断言,可能带来一些性能开销。

泛型

定义和使用

泛型是Go 1.18引入的特性,允许在函数和数据结构中使用类型参数。泛型通过类型参数列表(使用方括号 [] 包裹)来定义。

示例:

func PrintSlice[T any](s []T) {for _, v := range s {fmt.Println(v)}
}func main() {PrintSlice([]int{1, 2, 3})PrintSlice([]string{"hello", "world"})
}

在这个示例中,PrintSlice 是一个泛型函数,它接受一个类型参数 T,可以处理任意类型的切片。

优点

  • 类型安全性:泛型在编译时确定类型,确保类型安全。
  • 性能优化:由于在编译时已经确定类型,避免了运行时的类型检查和类型断言。

缺点

  • 复杂性:相比空接口,泛型的语法和概念更复杂。
  • 局限性:需要Go 1.18及以上版本支持。

共同点

  • 多类型处理:空接口和泛型都可以用于编写处理多种类型的代码,提高代码的重用性。
  • 函数和数据结构:两者都可以用于定义泛型函数和泛型数据结构。

区别

  • 类型安全性:空接口在运行时进行类型检查,泛型在编译时进行类型检查。泛型提供了更好的类型安全性。
  • 性能:由于泛型在编译时已经确定类型,没有运行时的类型检查和断言,因此性能通常优于空接口。
  • 灵活性:空接口更加灵活,因为它可以接受任何类型,而泛型需要通过类型参数来明确指定。
  • 代码复杂性:使用空接口的代码通常较为简单,而泛型代码由于引入了类型参数,可能更加复杂。

示例对比

空接口示例

func Print(value interface{}) {switch v := value.(type) {case int:fmt.Println("int:", v)case string:fmt.Println("string:", v)default:fmt.Println("unknown type")}
}func main() {Print(42)Print("hello")Print(3.14)
}

泛型示例

func Print[T any](value T) {fmt.Println(value)
}func main() {Print(42)Print("hello")Print(3.14)
}

在空接口示例中,需要进行类型断言来区分不同类型,而在泛型示例中,类型在编译时已经确定,代码更加简洁和类型安全。

结论

空接口和泛型是Go语言中处理多种类型的两种不同方式。空接口提供了更大的灵活性,但在类型安全性和性能上有所欠缺。泛型则提供了更高的类型安全性和更好的性能,但增加了代码的复杂性。

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

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

相关文章

Uniapp中image的@load不触发问题

load 事件不触发的常见情况有以下几种: 图片缓存命中 当图片从浏览器缓存中加载时,load 事件通常不会被触发。这是因为浏览器认为这个图片已经成功加载过了,所以不会再次触发 load 事件。 图片地址未发生变化 如果 image 组件的 src 属性值没有发生变化,即使图片是从网络上加载…

C++——关于new和delete

在C语言中我们想要动态管理内存空间,需要使用到malloc/calloc/realloc/free这些函数,在 C中有新的管理方式,那就是new和delete。new和delete是C新定义的操作符,专门用于管理堆上的内存空间。 new和delete在编译时,编译…

Linux C++ 055-设计模式之状态模式

Linux C 055-设计模式之状态模式 本节关键字:Linux、C、设计模式、状态模式 相关库函数: 概念 状态模式(State Pattern)是设计模式的一种,属于行为模式。允许一个对象在其内部状态改变时改变它的行为。对象看起来似…

Rust RefCell<T> 和内部可变性模式

内部可变性(Interior mutability)是 Rust 中的一个设计模式,它允许你即使在有不可变引用时也可以改变数据,这通常是借用规则所不允许的。为了改变数据,该模式在数据结构中使用 unsafe 代码来模糊 Rust 通常的可变性和借…

移动UI:具备什么特征,可以被认定为科技风格。

移动UI设计在科技风格上通常具备以下特征: 1. 清晰简洁的排版: 科技风格的移动UI通常采用清晰简洁的排版,注重信息的层次感和结构化,以便用户能够快速、直观地获取所需信息。 2. 几何形状和线条: 科技风格的移动UI常…

【算法】代码随想录之哈希表(更新中)

文章目录 前言 一、有效的字母异位词(LeetCode--242) 二、两个数组的交集(LeetCode--349) 前言 跟随代码随想录,学习哈希表相关的算法题目,记录学习过程中的tips。 一、有效的字母异位词(Le…

HashMap的扩容原理

1.7版本 1.先生成新数组 2.遍历老数组中的每个位置上的链表上的每个元素 3.取每个元素的key,并基于新数组长度,计算出每个元素在新数组中的下标 4.将元素添加到新数组中去 5.所有元素转移完了之后,将新数组赋值给HashMap对象的table属性 1.8版…

conda 复现论文部署环境常用操作

conda创建环境 conda create -n baichuan python3.9#不要创建环境,新创建的环境都没有关联cuda。可以复制原本就有cuda的环境 conda create -n 新环境名 --clone 旧环境名conda删除、查看环境 conda env list conda remove -n baichuan --all 停用环境 conda dea…

更新:彩虹云商城系统 自助下单免授权无后门源码(修复完整版)

源码简介: 最新更新彩虹云商城系统,自助下单免授权无后门源码(修复完整版) 自助下单彩虹云商城系统。这玩意儿不简单,它是高效稳定的电商平台!免授权源码版本,灵活方便。源码是用PHP语言写的。…

如何在SpringCloud中优雅实现服务注册与发现

Spring Cloud提供了一个名为Eureka的服务注册与发现组件,可以帮助我们在微服务架构中实现服务注册与发现的功能。在本文中,我将介绍如何在Spring Cloud中使用Eureka实现服务注册与发现,并通过一些优雅的方式来进行配置和使用。 首先&#xf…

定期整理pycharm相关缓存

缘起:下载一个数据集之后,点了虚拟机,直接卡住了,第二屏黑了,然后放到桌面,用电脑管家查了下,结果一直再查pycharm下的remote_sources https://blog.csdn.net/lt_BeiMo/article/details/124159…

迅为3A5000_7A2000ATX标准DIY国产龙芯电脑

性能强 采用全国产龙芯3A5000处理器,基于龙芯自主指令系统 (LoongArch)的LA464微结构,并进一步提升频率,降低功耗,优化性能。 桥片 采用龙芯 7A2000,支持PCIE 3.0、USB 3.0和 SATA 3.0.显示接口2 路、HDMI 和1路 VGA&a…

超算网络体系架构-资源层-平台层-服务层-应用层

目录 超算网络体系架构 我国超算基础设施 超算互联网相关标准研制方面 技术架构 资源层 基础资源 芯片多样 体系异构 高效存储 高速互连 资源池化 可隔离 可计量 互联网络 高带宽 低时延 高安全 平台层 算力接入 资源管理 算力调度 用户管理 交易管理 模…

计算机网络入门 --网络模型

计算机网络入门 --网络模型 1.OSI七层模型 1.1 模型概念 OSI七层模型是将计算机网络通信协议划分为七个不同层次的标准化框架,每一层都负责不同功能,并从物理连接层开始处理。OSI七层网络模型如下分别为:物理层、数据链路层、网络层、传输…

【linux】报错解决:配置RAIDA1之后系统识别不到

【linux】报错解决:配置RAIDA1之后系统识别不到 一、问题描述: 我的主板是华南金牌X99-F8D PLUS,安装了ubuntu20.04,通过BIOS创建了RAID1数组,进入系统之后识别不到我创建的RAID1数组。 二、原因分析: 可…

MySQL基础查询(DQL)

在查询之前,先看一下我的表内容和数据,一下都是参照我的表的数据来做个样例。我这个表名我自己起为emp 1.查询多个字段 (1)这个也就是可以随机想查自己想要的字段,可以是全部 SELECT 字段1,字段2,字段3..... FROM 表名例如我想…

RK3568 V1.4.0 SDK,USB OTG端子不能被电脑识别出adb设备,解决

修改后的/usr/bin/usbdevice: #!/bin/sh # # Usage: # usbdevice [start|update|stop] # # Hookable stages: # usb_<pre|post>_<init|prepare|start|stop|restart>_hook # <usb function>_<pre|post>_<prepare|start|stop>_hook # # Example …

Hadoop-32 ZooKeeper 分布式锁问题 分布式锁Java实现 附带案例和实现思路代码

章节内容 上节我们完成了&#xff1a; ZooKeeper的Leader选举机制ZooKeeper的选举过程ZooKeeper的ZAB协议 背景介绍 这里是三台公网云服务器&#xff0c;每台 2C4G&#xff0c;搭建一个Hadoop的学习环境&#xff0c;供我学习。 之前已经在 VM 虚拟机上搭建过一次&#xff0…

【Linux】多线程_9

文章目录 九、多线程10. 线程池 未完待续 九、多线程 10. 线程池 这里我没实现一些 懒汉单例模式 的线程池&#xff0c;并且包含 日志打印 的线程池&#xff1a; Makefile&#xff1a; threadpool:Main.ccg -o $ $^ -stdc11 -lpthread .PHONY:clean clean:rm -f threadpoolT…

element ui中el-form-item的属性rules的用法

目录 el-form-item的属性rules的用法 栗子 总结 实践应用 一、 定义静态的校验规则 二、定义动态的校验规则 el-form-item的属性rules的用法 在学习element ui 的Form表单组件时&#xff0c;学到el-form-item也有rules属性&#xff0c;但是对应这个属性如何使用&#x…