golang 基础 泛型编程

打印 上一主题 下一主题

主题 529|帖子 529|积分 1587

(一) 示例1
  1. package _case
  2. import "fmt"
  3. // 定义用户类型的结构体
  4. type user struct {
  5.         ID   int64
  6.         Name string
  7.         Age  uint8
  8. }
  9. // 定义地址类型的结构体
  10. type address struct {
  11.         ID       int
  12.         Province string
  13.         City     string
  14. }
  15. // 集合转列表函数,接受一个 map,返回一个切片
  16. func mapToList[k comparable, T any](mp map[k]T) []T {
  17.         // 创建切片,长度与 map 的长度相同
  18.         list := make([]T, len(mp))
  19.         var i int
  20.         for _, data := range mp {
  21.                 list[i] = data
  22.                 i++
  23.         }
  24.         return list
  25. }
  26. // 打印通道内容的函数,接受一个通道并打印通道中的每一个数据
  27. func myPrintln[T any](ch chan T) {
  28.         for data := range ch {
  29.                 fmt.Println(data)
  30.         }
  31. }
  32. // 主函数,执行类型转换和打印
  33. func TTypeCase() {
  34.         // 创建一个用户类型的 map
  35.         userMp := make(map[int64]user, 0)
  36.         // 向 map 中添加用户数据
  37.         userMp[1] = user{ID: 1, Name: "heheda", Age: 18}
  38.         userMp[2] = user{ID: 2, Name: "Jerry", Age: 20}
  39.         userMp[3] = user{ID: 3, Name: "Tom", Age: 22}
  40.         // 将用户 map 转换为用户列表
  41.         userList := mapToList[int64, user](userMp)
  42.         // 创建用户类型的通道,并启动 goroutine 打印通道数据
  43.         ch := make(chan user)
  44.         go myPrintln(ch)
  45.         // 将用户列表中的数据发送到通道
  46.         for _, u := range userList {
  47.                 ch <- u
  48.         }
  49.         close(ch)
  50.         // 创建一个地址类型的 map
  51.         addrMp := make(map[int64]address, 0)
  52.         // 向 map 中添加地址数据
  53.         addrMp[1] = address{ID: 1, Province: "湖南", City: "长沙"}
  54.         addrMp[2] = address{ID: 2, Province: "广东", City: "揭阳"}
  55.         // 将地址 map 转换为地址列表
  56.         addrList := mapToList[int64, address](addrMp)
  57.         // 创建地址类型的通道,并启动 goroutine 打印通道数据
  58.         ch1 := make(chan address)
  59.         go myPrintln(ch1)
  60.         // 将地址列表中的数据发送到通道
  61.         for _, addr := range addrList {
  62.                 ch1 <- addr
  63.         }
  64.         close(ch1)
  65. }
  66. // 泛型切片的定义
  67. type List[T any] []T
  68. // 泛型 map 的定义
  69. // 声明两个泛型,分别为 k 、 v
  70. type MapT[k comparable, v any] map[k]v
  71. // 泛型通道的定义
  72. type Chan[T any] chan T
  73. func TTypeCase1() {
  74.         // map[int64]user -> MapT[int64, user]
  75.         userMp := make(MapT[int64, user], 0)
  76.         userMp[1] = user{ID: 1, Name: "heheda", Age: 18}
  77.         userMp[2] = user{ID: 2, Name: "Jerry", Age: 20}
  78.         userMp[3] = user{ID: 3, Name: "Tom", Age: 22}
  79.         var userList List[user]
  80.         userList = mapToList[int64, user](userMp)
  81.         ch := make(Chan[user])
  82.         go myPrintln(ch)
  83.         for _, u := range userList {
  84.                 ch <- u
  85.         }
  86.         close(ch)
  87.         // map[int64]address -> MapT[int64, address]
  88.         addrMp := make(MapT[int64, address], 0)
  89.         addrMp[1] = address{ID: 1, Province: "湖南", City: "长沙"}
  90.         addrMp[2] = address{ID: 2, Province: "广东", City: "揭阳"}
  91.         var addrList List[address]
  92.         addrList = mapToList[int64, address](addrMp)
  93.         ch1 := make(Chan[address])
  94.         go myPrintln(ch1)
  95.         for _, addr := range addrList {
  96.                 ch1 <- addr
  97.         }
  98.         close(ch1)
  99. }
复制代码

这段代码展示了如何使用 Go 语言中的泛型、结构体和通道举行数据处理和打印。以下是对代码各部分的解释:
1.包和导入
  1. package _case
  2. import "fmt"
复制代码


  • 导入 fmt 包用于格式化输入输出
 2.结构体定义


  • user 结构体 有三个字段:ID(范例 int64)、Name(范例 string)和 Age(范例 uint8)
  • address 结构体 有三个字段:ID(范例 int)、Province(范例 string)和 City(范例 string)
 3.泛型函数
  1. func mapToList[k comparable, T any](mp map[k]T) []T {
  2.     list := make([]T, len(mp))
  3.     var i int
  4.     for _, data := range mp {
  5.         list[i] = data
  6.         i++
  7.     }
  8.     return list
  9. }
复制代码


  • mapToList 是一个泛型函数,它担当一个 map 范例的参数 mp,并将其转换为切片(列表)
  • 这里使用了两个泛型范例参数:k(可比较范例)和 T(恣意范例)
4.打印通道内容的泛型函数
  1. func myPrintln[T any](ch chan T) {
  2.     for data := range ch {
  3.         fmt.Println(data)
  4.     }
  5. }
复制代码


  • myPrintln 是一个泛型函数,担当一个通道范例的参数 ch,并打印通道中的每一个数据
5.主函数 TTypeCase
  1. func TTypeCase() {
  2.     userMp := make(map[int64]user, 0)
  3.     userMp[1] = user{ID: 1, Name: "heheda", Age: 18}
  4.     userMp[2] = user{ID: 2, Name: "Jerry", Age: 20}
  5.     userMp[3] = user{ID: 3, Name: "Tom", Age: 22}
  6.     userList := mapToList[int64, user](userMp)
  7.    
  8.     ch := make(chan user)
  9.     go myPrintln(ch)
  10.     for _, u := range userList {
  11.         ch <- u
  12.     }
  13.     close(ch)
  14.    
  15.     addrMp := make(map[int64]address, 0)
  16.     addrMp[1] = address{ID: 1, Province: "湖南", City: "长沙"}
  17.     addrMp[2] = address{ID: 2, Province: "广东", City: "揭阳"}
  18.     addrList := mapToList[int64, address](addrMp)
  19.    
  20.     ch1 := make(chan address)
  21.     go myPrintln(ch1)
  22.     for _, addr := range addrList {
  23.         ch1 <- addr
  24.     }
  25.     close(ch1)
  26. }
复制代码


  • 这个函数首先创建两个 map 分别存储用户和地址数据
  • 使用 mapToList 将 map 转换为列表(切片)
  • 创建通道并启动 goroutine 打印通道数据
  • 将列表中的数据写入通道并关闭通道
 6.型范例


  • 定义三个新的泛型范例:List、MapT 和 Chan
  1. type List[T any] []T
  2. type MapT[k comparable, v any] map[k]v
  3. type Chan[T any] chan T
复制代码
7.另一主函数 TTypeCase1
  1. func TTypeCase1() {
  2.     // map[int64]user -> MapT[int64, user]
  3.     userMp := make(MapT[int64, user], 0)
  4.     userMp[1] = user{ID: 1, Name: "heheda", Age: 18}
  5.     userMp[2] = user{ID: 2, Name: "Jerry", Age: 20}
  6.     userMp[3] = user{ID: 3, Name: "Tom", Age: 22}
  7.    
  8.     var userList List[user]
  9.     userList = mapToList[int64, user](userMp)
  10.    
  11.     ch := make(Chan[user])
  12.     go myPrintln(ch)
  13.     for _, u := range userList {
  14.         ch <- u
  15.     }
  16.     close(ch)
  17.    
  18.     // map[int64]address -> MapT[int64, address]
  19.     addrMp := make(MapT[int64, address], 0)
  20.     addrMp[1] = address{ID: 1, Province: "湖南", City: "长沙"}
  21.     addrMp[2] = address{ID: 2, Province: "广东", City: "揭阳"}
  22.    
  23.     var addrList List[address]
  24.     addrList = mapToList[int64, address](addrMp)
  25.    
  26.     ch1 := make(Chan[address])
  27.     go myPrintln(ch1)
  28.     for _, addr := range addrList {
  29.         ch1 <- addr
  30.     }
  31.     close(ch1)
  32. }
复制代码


  • TTypeCase1 函数与 TTypeCase 类似,但使用了自定义的泛型范例 MapT 和 List
相关知识点



  • 泛型 泛型允许函数和数据结构定义中使用范例参数,从而提拔代码的复用性
  • 结构体 结构体是 Go 中用于将多个字段组合成一个单一范例的数据结构
  • 通道(Channel)通道是在 goroutine 之间通报数据的管道,可以同步或者异步
  • goroutine goroutine 是 Go 中轻量级的线程,用于并发编程
  • map map 是一种内建的数据结构,用于存储键值对
可以实验运行和修改代码,进一步理解这些概念。
(二) 示例2
  1. package _case
  2. 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 {
  3.         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.包和导入
  1. package _case
  2. import "fmt"
复制代码
 导入 fmt 包用于格式化输入输出。
2.定义基本接口
  1. type ToString interface {
  2.     String() string
  3. }
复制代码
3.实现 ToString 接口
  1. func (u user) String() string {
  2.     return fmt.Sprintf("ID: %d,Name: %s,Age: %d", u.ID, u.Name, u.Age)
  3. }
  4. func (addr address) String() string {
  5.     return fmt.Sprintf("ID: %d,Province: %s,City: %s", addr.ID, addr.Province, addr.City)
  6. }
复制代码


  • user 和 address 结构体实现了 ToString 接口的 String 方法,返回结构体的字符串体现。
 4.定义泛型接口
  1. type GetKey[T comparable] interface {
  2.     any
  3.     Get() T
  4. }
复制代码
这是一个泛型接口声明。具体来说,GetKey 接口担当一个范例参数 T,这个范例参数必须是一个可比较的范例 (comparable)
(1)定义泛型接口
  1. type GetKey[T comparable] interface {
复制代码


  • GetKey 定义了一个泛型接口。
  • T 是一个范例参数,它必须是一个可比较的范例 (comparable)。可比较范例体现可以使用 == 和 != 运算符举行比较,常见的可比较范例包括整数、浮点数、字符串以及指针等。
(2)嵌入 any 接口


  • Go 1.18 引入了范例聚集 any,它是 interface{} 的别名,体现恣意范例
  • 在接口中嵌入 any 意味着该接口可以担当任何范例的实现
(3)定义方法
  1. Get() T
复制代码


  • 定义了一个方法 Get,这个方法返回范例 T。
  • 因为 T 是一个范例参数,所以 Get 方法的返回值范例是泛型的,可以是恣意 T 范例
5.实现 GetKey 接口
  1. func (u user) Get() int64 {
  2.     return u.ID
  3. }
  4. func (addr address) Get() int {
  5.     return addr.ID
  6. }
复制代码


  • user 和 address 结构体实现了 GetKey 接口的 Get 方法,分别返回结构体的 ID 字段
  • user 实现了 GetKey[int64] 接口,而 address 实现了 GetKey[int] 接口
 6.列表转聚集函数
  1. func listToMap[k comparable, T GetKey[k]](list []T) map[k]T {
  2.     mp := make(MapT[k, T], len(list))
  3.     for _, data := range list {
  4.         mp[data.Get()] = data
  5.     }
  6.     return mp
  7. }
复制代码


  • listToMap 是一个泛型函数,将 list 转换为 map
  • 函数参数 k 为键的范例,T 为实现 GetKey[k] 接口的范例
  • data.Get() 返回键,作为 map 的键
 7.主函数 InterfaceCase
  1. func InterfaceCase() {
  2.     userList := []GetKey[int64]{
  3.         user{ID: 1, Name: "张三", Age: 18},
  4.         user{ID: 2, Name: "李四", Age: 19},
  5.     }
  6.     addrList := []GetKey[int]{
  7.         address{ID: 1, Province: "广东", City: "揭阳"},
  8.         address{ID: 2, Province: "湖南", City: "长沙"},
  9.     }
  10.     userMp := listToMap[int64, GetKey[int64]](userList)
  11.     fmt.Println(userMp)
  12.     addrMp := listToMap[int, GetKey[int]](addrList)
  13.     fmt.Println(addrMp)
  14. }
复制代码


  • 创建 userList 和 addrList,分别包罗 user 和 address 结构体
  • 调用 listToMap 将列表转换为 map,并打印效果
>>分解解读
(1)创建 user 结构体实例
  1. user{ID: 1, Name: "张三", Age: 18},
  2. 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)创建并初始化切片
  1. userList := []GetKey[int64]{
  2.     user{ID: 1, Name: "张三", Age: 18},
  3.     user{ID: 2, Name: "李四", Age: 19},
  4. }
复制代码


  • 使用两个 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
  1. package _receiver
  2. import "fmt"
  3. // 定义一个泛型结构体 MyStruct
  4. // 该结构体接受一个类型参数 T,T 只能是 *int 或 *string
  5. type MyStruct[T interface{ *int | *string }] struct {
  6.         Name string // 结构体的名字字段
  7.         Data T      // 结构体的数据字段,类型为 T
  8. }
  9. // 定义 MyStruct 结构体的泛型方法接收器
  10. // GetData 返回结构体中的 Data 字段
  11. func (myStruct MyStruct[T]) GetData() T {
  12.         return myStruct.Data
  13. }
  14. // 主函数,演示 MyStruct 结构体和其方法的使用
  15. func ReceiverCase() {
  16.         // 定义一个整型变量,并创建 MyStruct 实例
  17.         data := 18
  18.         myStruct := MyStruct[*int]{
  19.                 Name: "heheda",
  20.                 Data: &data, // 将整型变量的指针分配给 Data 字段
  21.         }
  22.         // 调用 GetData 方法获取 Data 字段的值并打印
  23.         data1 := myStruct.GetData()
  24.         fmt.Println(*data1) // 解引用指针打印值,输出 18
  25.         // 定义一个字符串变量,并创建 MyStruct 实例
  26.         str := "abcdefg"
  27.         myStruct1 := MyStruct[*string]{
  28.                 Name: "heheda",
  29.                 Data: &str, // 将字符串变量的指针分配给 Data 字段
  30.         }
  31.         // 调用 GetData 方法获取 Data 字段的值并打印
  32.         str1 := myStruct1.GetData()
  33.         fmt.Println(*str1) // 解引用指针打印值,输出 "abcdefg"
  34. }
复制代码
这段代码展示了如安在 Go 语言中使用泛型定义结构体和方法接收器,以下是对代码各部分的解释:
1.包和导入
  1. package _receiver
  2. import "fmt"
复制代码
导入 fmt 包用于格式化输入输出
2.定义泛型结构体
  1. type MyStruct[T interface{ *int | *string }] struct {
  2.     Name string
  3.     Data T
  4. }
复制代码


  • MyStruct 是一个泛型结构体,结构体内包罗两个字段:Name(范例为 string)和 Data(泛型范例 T)
  • 这里 T 被约束为 *int 或 *string 范例
 3.定义泛型方法接收器
  1. func (myStruct MyStruct[T]) GetData() T {
  2.     return myStruct.Data
  3. }
复制代码


  • GetData 是 MyStruct 的方法,这个方法担当一个泛型范例 T,返回 MyStruct 结构体中的 Data 字段
 4.主函数 ReceiverCase
  1. func ReceiverCase() {
  2.     data := 18
  3.     myStruct := MyStruct[*int]{
  4.         Name: "heheda",
  5.         Data: &data,
  6.     }
  7.     data1 := myStruct.GetData()
  8.     fmt.Println(*data1)
  9.     str := "abcdefg"
  10.     myStruct1 := MyStruct[*string]{
  11.         Name: "heheda",
  12.         Data: &str,
  13.     }
  14.     str1 := myStruct1.GetData()
  15.     fmt.Println(*str1)
  16. }
复制代码


  • 创建一个整型变量 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
  1. package main
  2. import (
  3.         "context"
  4.         _case "gomod/genetic-T/case"
  5.         "os"
  6.         "os/signal"
  7. )
  8. func main() {
  9.         _case.TTypeCase()
  10.         _case.TTypeCase1()
  11.         _case.InterfaceCase()
  12.         _receiver.ReceiverCase()
  13.         ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, os.Kill)
  14.         defer stop()
  15.         <-ctx.Done()
  16. }
复制代码


免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

耶耶耶耶耶

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表