ToB企服应用市场:ToB评测及商务社交产业平台
标题:
golang 基础 泛型编程
[打印本页]
作者:
耶耶耶耶耶
时间:
2024-7-23 08:48
标题:
golang 基础 泛型编程
(一) 示例1
package _case
import "fmt"
// 定义用户类型的结构体
type user struct {
ID int64
Name string
Age uint8
}
// 定义地址类型的结构体
type address struct {
ID int
Province string
City string
}
// 集合转列表函数,接受一个 map,返回一个切片
func mapToList[k comparable, T any](mp map[k]T) []T {
// 创建切片,长度与 map 的长度相同
list := make([]T, len(mp))
var i int
for _, data := range mp {
list[i] = data
i++
}
return list
}
// 打印通道内容的函数,接受一个通道并打印通道中的每一个数据
func myPrintln[T any](ch chan T) {
for data := range ch {
fmt.Println(data)
}
}
// 主函数,执行类型转换和打印
func TTypeCase() {
// 创建一个用户类型的 map
userMp := make(map[int64]user, 0)
// 向 map 中添加用户数据
userMp[1] = user{ID: 1, Name: "heheda", Age: 18}
userMp[2] = user{ID: 2, Name: "Jerry", Age: 20}
userMp[3] = user{ID: 3, Name: "Tom", Age: 22}
// 将用户 map 转换为用户列表
userList := mapToList[int64, user](userMp)
// 创建用户类型的通道,并启动 goroutine 打印通道数据
ch := make(chan user)
go myPrintln(ch)
// 将用户列表中的数据发送到通道
for _, u := range userList {
ch <- u
}
close(ch)
// 创建一个地址类型的 map
addrMp := make(map[int64]address, 0)
// 向 map 中添加地址数据
addrMp[1] = address{ID: 1, Province: "湖南", City: "长沙"}
addrMp[2] = address{ID: 2, Province: "广东", City: "揭阳"}
// 将地址 map 转换为地址列表
addrList := mapToList[int64, address](addrMp)
// 创建地址类型的通道,并启动 goroutine 打印通道数据
ch1 := make(chan address)
go myPrintln(ch1)
// 将地址列表中的数据发送到通道
for _, addr := range addrList {
ch1 <- addr
}
close(ch1)
}
// 泛型切片的定义
type List[T any] []T
// 泛型 map 的定义
// 声明两个泛型,分别为 k 、 v
type MapT[k comparable, v any] map[k]v
// 泛型通道的定义
type Chan[T any] chan T
func TTypeCase1() {
// map[int64]user -> MapT[int64, user]
userMp := make(MapT[int64, user], 0)
userMp[1] = user{ID: 1, Name: "heheda", Age: 18}
userMp[2] = user{ID: 2, Name: "Jerry", Age: 20}
userMp[3] = user{ID: 3, Name: "Tom", Age: 22}
var userList List[user]
userList = mapToList[int64, user](userMp)
ch := make(Chan[user])
go myPrintln(ch)
for _, u := range userList {
ch <- u
}
close(ch)
// map[int64]address -> MapT[int64, address]
addrMp := make(MapT[int64, address], 0)
addrMp[1] = address{ID: 1, Province: "湖南", City: "长沙"}
addrMp[2] = address{ID: 2, Province: "广东", City: "揭阳"}
var addrList List[address]
addrList = mapToList[int64, address](addrMp)
ch1 := make(Chan[address])
go myPrintln(ch1)
for _, addr := range addrList {
ch1 <- addr
}
close(ch1)
}
复制代码
这段代码展示了如何使用 Go 语言中的泛型、结构体和通道举行数据处理和打印。以下是对代码各部分的解释:
1.包和导入
package _case
import "fmt"
复制代码
导入 fmt 包用于格式化输入输出
2.结构体定义
user 结构体 有三个字段:ID(范例 int64)、Name(范例 string)和 Age(范例 uint8)
address 结构体 有三个字段:ID(范例 int)、Province(范例 string)和 City(范例 string)
3.泛型函数
func mapToList[k comparable, T any](mp map[k]T) []T {
list := make([]T, len(mp))
var i int
for _, data := range mp {
list[i] = data
i++
}
return list
}
复制代码
mapToList 是一个泛型函数,它担当一个 map 范例的参数 mp,并将其转换为切片(列表)
这里使用了两个泛型范例参数:k(可比较范例)和 T(恣意范例)
4.
打印通道内容的泛型函数
func myPrintln[T any](ch chan T) {
for data := range ch {
fmt.Println(data)
}
}
复制代码
myPrintln 是一个泛型函数,担当一个通道范例的参数 ch,并打印通道中的每一个数据
5.
主函数 TTypeCase
func TTypeCase() {
userMp := make(map[int64]user, 0)
userMp[1] = user{ID: 1, Name: "heheda", Age: 18}
userMp[2] = user{ID: 2, Name: "Jerry", Age: 20}
userMp[3] = user{ID: 3, Name: "Tom", Age: 22}
userList := mapToList[int64, user](userMp)
ch := make(chan user)
go myPrintln(ch)
for _, u := range userList {
ch <- u
}
close(ch)
addrMp := make(map[int64]address, 0)
addrMp[1] = address{ID: 1, Province: "湖南", City: "长沙"}
addrMp[2] = address{ID: 2, Province: "广东", City: "揭阳"}
addrList := mapToList[int64, address](addrMp)
ch1 := make(chan address)
go myPrintln(ch1)
for _, addr := range addrList {
ch1 <- addr
}
close(ch1)
}
复制代码
这个函数首先创建两个 map 分别存储用户和地址数据
使用 mapToList 将 map 转换为列表(切片)
创建通道并启动 goroutine 打印通道数据
将列表中的数据写入通道并关闭通道
6.
泛
型范例
定义三个新的泛型范例:List、MapT 和 Chan
type List[T any] []T
type MapT[k comparable, v any] map[k]v
type Chan[T any] chan T
复制代码
7.另一主函数 TTypeCase1
func TTypeCase1() {
// map[int64]user -> MapT[int64, user]
userMp := make(MapT[int64, user], 0)
userMp[1] = user{ID: 1, Name: "heheda", Age: 18}
userMp[2] = user{ID: 2, Name: "Jerry", Age: 20}
userMp[3] = user{ID: 3, Name: "Tom", Age: 22}
var userList List[user]
userList = mapToList[int64, user](userMp)
ch := make(Chan[user])
go myPrintln(ch)
for _, u := range userList {
ch <- u
}
close(ch)
// map[int64]address -> MapT[int64, address]
addrMp := make(MapT[int64, address], 0)
addrMp[1] = address{ID: 1, Province: "湖南", City: "长沙"}
addrMp[2] = address{ID: 2, Province: "广东", City: "揭阳"}
var addrList List[address]
addrList = mapToList[int64, address](addrMp)
ch1 := make(Chan[address])
go myPrintln(ch1)
for _, addr := range addrList {
ch1 <- addr
}
close(ch1)
}
复制代码
TTypeCase1 函数与 TTypeCase 类似,但使用了自定义的泛型范例 MapT 和 List
相关知识点
泛型
泛型允许函数和数据结构定义中使用范例参数,从而提拔代码的复用性
结构体
结构体是 Go 中用于将多个字段组合成一个单一范例的数据结构
通道(Channel)
通道是在 goroutine 之间通报数据的管道,可以同步或者异步
goroutine
goroutine 是 Go 中轻量级的线程,用于并发编程
map
map 是一种内建的数据结构,用于存储键值对
可以实验运行和修改代码,进一步理解这些概念。
(二) 示例2
package _case
import "fmt"// 定义接口 ToString,有一个 String() 方法type ToString interface { String() string}// user 结构体实现 ToString 接口func (u user) String() string { return fmt.Sprintf("ID: %d,Name: %s,Age: %d", u.ID, u.Name, u.Age)}// address 结构体实现 ToString 接口func (addr address) String() string { return fmt.Sprintf("ID: %d,Province: %s,City: %s", addr.ID, addr.Province, addr.City)}// 定义泛型接口 GetKey,要求实现 Get() 方法返回范例 Ttype GetKey[T comparable] interface {
any Get() T}// user 结构体实现 GetKey 接口,Get() 返回 IDfunc (u user) Get() int64 { return u.ID}// address 结构体实现 GetKey 接口,Get() 返回 IDfunc (addr address) Get() int { return addr.ID}// 泛型函数 listToMap,将列表转换为 mapfunc listToMap[k comparable, T GetKey[k]](list []T) map[k]T { mp := make(map[k]T, len(list)) // 创建 map,长度为列表长度 for _, data := range list { mp[data.Get()] = data // 使用 Get() 方法获取键 } return mp}// 主函数,演示列表转 map 的操纵func InterfaceCase() { // 创建 user 列表,元素实现了 GetKey[int64] 接口 userList := []GetKey[int64]{ user{ID: 1, Name: "张三", Age: 18}, user{ID: 2, Name: "李四", Age: 19}, } // 创建 address 列表,元素实现了 GetKey[int] 接口 addrList := []GetKey[int]{ address{ID: 1, Province: "广东", City: "揭阳"}, address{ID: 2, Province: "湖南", City: "长沙"}, } // 将 user 列表转换为 map,并打印效果 userMp := listToMap[int64, GetKey[int64]](userList) fmt.Println(userMp) // 将 address 列表转换为 map,并打印效果 addrMp := listToMap[int, GetKey[int]](addrList) fmt.Println(addrMp)}
复制代码
1.
包和导入
package _case
import "fmt"
复制代码
导入 fmt 包用于格式化输入输出。
2.定义基本接口
type ToString interface {
String() string
}
复制代码
3.
实现 ToString 接口
func (u user) String() string {
return fmt.Sprintf("ID: %d,Name: %s,Age: %d", u.ID, u.Name, u.Age)
}
func (addr address) String() string {
return fmt.Sprintf("ID: %d,Province: %s,City: %s", addr.ID, addr.Province, addr.City)
}
复制代码
user 和 address 结构体实现了 ToString 接口的 String 方法,返回结构体的字符串体现。
4.
定义泛型接口
type GetKey[T comparable] interface {
any
Get() T
}
复制代码
这是一个泛型接口声明。具体来说,GetKey 接口担当一个范例参数 T,这个范例参数必须是一个可比较的范例 (comparable)
(1)
定义泛型接口
type GetKey[T comparable] interface {
复制代码
GetKey 定义了一个泛型接口。
T 是一个范例参数,它必须是一个可比较的范例 (comparable)。可比较范例体现可以使用 == 和 != 运算符举行比较,常见的可比较范例包括整数、浮点数、字符串以及指针等。
(2)嵌入 any 接口
Go 1.18 引入了范例聚集 any,它是 interface{} 的别名,体现恣意范例
在接口中嵌入 any 意味着该接口可以担当任何范例的实现
(3)
定义方法
Get() T
复制代码
定义了一个方法 Get,这个方法返回范例 T。
因为 T 是一个范例参数,所以 Get 方法的返回值范例是泛型的,可以是恣意 T 范例
5.实现 GetKey 接口
func (u user) Get() int64 {
return u.ID
}
func (addr address) Get() int {
return addr.ID
}
复制代码
user 和 address 结构体实现了 GetKey 接口的 Get 方法,分别返回结构体的 ID 字段
user 实现了 GetKey[int64] 接口,而 address 实现了 GetKey[int] 接口
6.
列表转聚集函数
func listToMap[k comparable, T GetKey[k]](list []T) map[k]T {
mp := make(MapT[k, T], len(list))
for _, data := range list {
mp[data.Get()] = data
}
return mp
}
复制代码
listToMap 是一个泛型函数,将 list 转换为 map
函数参数 k 为键的范例,T 为实现 GetKey[k] 接口的范例
data.Get() 返回键,作为 map 的键
7.
主函数 InterfaceCase
func InterfaceCase() {
userList := []GetKey[int64]{
user{ID: 1, Name: "张三", Age: 18},
user{ID: 2, Name: "李四", Age: 19},
}
addrList := []GetKey[int]{
address{ID: 1, Province: "广东", City: "揭阳"},
address{ID: 2, Province: "湖南", City: "长沙"},
}
userMp := listToMap[int64, GetKey[int64]](userList)
fmt.Println(userMp)
addrMp := listToMap[int, GetKey[int]](addrList)
fmt.Println(addrMp)
}
复制代码
创建 userList 和 addrList,分别包罗 user 和 address 结构体
调用 listToMap 将列表转换为 map,并打印效果
>>分解解读
(1)创建 user 结构体实例
user{ID: 1, Name: "张三", Age: 18},
user{ID: 2, Name: "李四", Age: 19},
复制代码
user{ID: 1, Name: "张三", Age: 18} 创建一个 user 实例,ID 为 1,名字为 "张三",年龄为 18。
user{ID: 2, Name: "李四", Age: 19} 创建一个 user 实例,ID 为 2,名字为 "李四",年龄为 19。
(2)实现 GetKey[int64] 接口
上述 user 结构体实现了 GetKey[int64] 接口,因为 user 结构体定义了 Get() 方法,而且返回值范例为 int64
(3)创建并初始化切片
userList := []GetKey[int64]{
user{ID: 1, Name: "张三", Age: 18},
user{ID: 2, Name: "李四", Age: 19},
}
复制代码
使用两个 user 实例来初始化一个切片 (slice)。
该切片的范例是 []GetKey[int64],体现这个切片包罗实现了 GetKey[int64] 接口的元素。
(4)作用
创建并初始化一个包罗多个 user 实例的切片,让这个切片可以作为 GetKey[int64] 接口的实现来通报和使用。
这样的设计允许切片中的每个 user 实例都具备 Get 方法,从而可以在后续的泛型函数 listToMap 中使用这些 Get 方法来获取唯一键。
总结
GetKey 是一个泛型接口,带有范例参数 T,该参数必须是可比较范例。
该接口要求实现者提供一个 Get 方法,返回范例为 T。
这是 Go 1.18 引入的泛型特性,允许编写更通用、更范例安全的代码。
相关知识点
接口
接口定义了方法聚集,恣意范例只要实现了这些方法,就实现了该接口
泛型
泛型允许定义通用的数据结构和函数,进步代码复用性。Go 1.18 开始引入泛型支持
泛型接口和方法通过范例参数举行约束
结构体
结构体是复合数据范例,将相关数据构造在一起
map
map 在 Go 中用于存储键值对,是内建的数据结构
导入包
fmt 包提供了格式化输入和输出功能,用于打印变量的值
(三) 示例3
package _receiver
import "fmt"
// 定义一个泛型结构体 MyStruct
// 该结构体接受一个类型参数 T,T 只能是 *int 或 *string
type MyStruct[T interface{ *int | *string }] struct {
Name string // 结构体的名字字段
Data T // 结构体的数据字段,类型为 T
}
// 定义 MyStruct 结构体的泛型方法接收器
// GetData 返回结构体中的 Data 字段
func (myStruct MyStruct[T]) GetData() T {
return myStruct.Data
}
// 主函数,演示 MyStruct 结构体和其方法的使用
func ReceiverCase() {
// 定义一个整型变量,并创建 MyStruct 实例
data := 18
myStruct := MyStruct[*int]{
Name: "heheda",
Data: &data, // 将整型变量的指针分配给 Data 字段
}
// 调用 GetData 方法获取 Data 字段的值并打印
data1 := myStruct.GetData()
fmt.Println(*data1) // 解引用指针打印值,输出 18
// 定义一个字符串变量,并创建 MyStruct 实例
str := "abcdefg"
myStruct1 := MyStruct[*string]{
Name: "heheda",
Data: &str, // 将字符串变量的指针分配给 Data 字段
}
// 调用 GetData 方法获取 Data 字段的值并打印
str1 := myStruct1.GetData()
fmt.Println(*str1) // 解引用指针打印值,输出 "abcdefg"
}
复制代码
这段代码展示了如安在 Go 语言中使用泛型定义结构体和方法接收器,以下是对代码各部分的解释:
1.包和导入
package _receiver
import "fmt"
复制代码
导入 fmt 包用于格式化输入输出
2.定义泛型结构体
type MyStruct[T interface{ *int | *string }] struct {
Name string
Data T
}
复制代码
MyStruct 是一个泛型结构体,结构体内包罗两个字段:Name(范例为 string)和 Data(泛型范例 T)
这里 T 被约束为 *int 或 *string 范例
3.定义泛型方法接收器
func (myStruct MyStruct[T]) GetData() T {
return myStruct.Data
}
复制代码
GetData 是 MyStruct 的方法,这个方法担当一个泛型范例 T,返回 MyStruct 结构体中的 Data 字段
4.主函数 ReceiverCase
func ReceiverCase() {
data := 18
myStruct := MyStruct[*int]{
Name: "heheda",
Data: &data,
}
data1 := myStruct.GetData()
fmt.Println(*data1)
str := "abcdefg"
myStruct1 := MyStruct[*string]{
Name: "heheda",
Data: &str,
}
str1 := myStruct1.GetData()
fmt.Println(*str1)
}
复制代码
创建一个整型变量 data 并取其指针赋值给 MyStruct 实例 myStruct 的 Data 字段
调用 GetData 方法获取 Data 字段的值并打印
创建一个字符串变量 str 并取其指针赋值给 MyStruct 实例 myStruct1 的 Data 字段
调用 GetData 方法获取 Data 字段的值并打印
相关知识点
(1)泛型
泛型允许定义通用的数据结构和函数,加强代码复用性。Go 1.18 开始引入泛型支持
泛型范例参数通过范例约束举行限制,如本例中的 interface{ *int | *string }
(2)结构体
结构体是一种复合数据范例,可以包罗多个字段,允许将相关的数据构造在一起
(3)方法接收器
方法接收器是指与某个范例(如结构体或接口)关联的方法。在本例中,我们定义了 MyStruct 范例的泛型方法接收器 GetData
(4)指针
指针保存了变量的内存地址,在 Go 中,使用 & 符号获取一个变量的指针,使用 * 符号解引用指针获取指针指向的值
(5)导入包
fmt 包提供了格式化输入和输出功能,用于打印变量的值
可以实验运行和修改代码,进一步理解这些概念。
main.go
package main
import (
"context"
_case "gomod/genetic-T/case"
"os"
"os/signal"
)
func main() {
_case.TTypeCase()
_case.TTypeCase1()
_case.InterfaceCase()
_receiver.ReceiverCase()
ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, os.Kill)
defer stop()
<-ctx.Done()
}
复制代码
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
欢迎光临 ToB企服应用市场:ToB评测及商务社交产业平台 (https://dis.qidao123.com/)
Powered by Discuz! X3.4