(一) 示例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() 方法返回类型 T
type GetKey[T comparable] interface {
any
Get() T
}
// user 结构体实现 GetKey 接口,Get() 返回 ID
func (u user) Get() int64 {
return u.ID
}
// address 结构体实现 GetKey 接口,Get() 返回 ID
func (addr address) Get() int {
return addr.ID
}
// 泛型函数 listToMap,将列表转换为 map
func 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()
}