Go语法基础(四)泛型

Go 语言的泛型功能是在 Go 1.18 版本中引入的。泛型允许你编写更加灵活和可重用的代码。在泛型的上下文中,"类型参数"被用来表示可以被多种数据类型替换的占位符。这使得你可以编写不依赖于特定类型的函数和数据结构。

基本概念

  1. 类型参数:这是定义泛型时使用的占位符类型。类型参数允许你编写出对多种数据类型都适用的代码。
  2. 类型约束:它用于限制类型参数可以接受的类型。这可以是一个接口,类型参数需要实现这个接口,或者是一组类型的联合。

泛型的使用

泛型主要应用在两个方面:函数和类型(比如结构体或接口)。

泛型函数

泛型函数可以对不同类型执行相同的操作。它们使用类型参数定义,这使得函数可以接受多种类型的参数。

例子:

go 复制代码
goCopy code
package main

import "fmt"

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

func main() {
    intSlice := []int{1, 2, 3}
    stringSlice := []string{"hello", "world"}
    //printSlice[int](intSlice)  
     printSlice(intSlice)//这里跟TS一样编译器可以根据传入的参数类型 来推断T的类型 
    printSlice(stringSlice)
}

这里,printSlice 函数通过 [T any] 泛型类型参数定义,可以处理任意类型的切片。

泛型类型

你还可以定义泛型类型,比如泛型结构体、接口、切片等。

例子:

go 复制代码
goCopy code
package main

import "fmt"

// 泛型结构体
type Holder[T any] struct {
    Value T
}

func main() {
    intHolder := Holder[int]{Value: 100}
    stringHolder := Holder[string]{Value: "Go Generics"}

    fmt.Println(intHolder)
    fmt.Println(stringHolder)
}

在这个例子中,Holder 是一个泛型结构体,可以持有任何类型的值。

泛型的优点

  1. 代码复用:允许你编写可与多种类型协同工作的代码,减少重复代码。
  2. 类型安全:尽管代码是泛型的,但在编译时仍然进行类型检查,减少运行时错误。
  3. 性能:由于在编译时确定类型,泛型代码通常可以达到与特定类型代码相同的性能。

注意事项

  1. 类型约束:合理使用类型约束可以让泛型代码更加安全和易于理解。
  2. 复杂度:虽然泛型提供了灵活性,但过度使用可能会导致代码难以理解和维护。

泛型是 Go 语言中一个强大的特性,可以极大地提高代码的可重用性和灵活性。通过泛型,你可以编写更加通用和灵活的代码库和应用程序。

复杂示例

在 Go 泛型中创建更复杂的类型约束,你可以定义一个接口,该接口结合了多个方法、嵌入其他接口或使用联合类型。这样的约束可以用于限制泛型类型参数必须满足的条件,从而使得你的泛型函数或类型更具表现力和安全性。

假设我们想创建一个泛型函数,它要求类型参数既要实现标准的 Stringer 接口(定义了 String() string 方法),又要实现自定义的 Resetter 接口(定义了 Reset() 方法)。我们可以这样定义类型约束:

go 复制代码
goCopy code
package main

import (
    "fmt"
    "strings"
)

// Stringer 是由 fmt 包定义的一个接口,要求有一个 String() string 方法。
type Stringer interface {
    String() string
}

// 定义自定义接口 Resetter。
type Resetter interface {
    Reset()
}

// 创建一个复合接口,要求同时满足 Stringer 和 Resetter。
type StringerAndResetter interface {
    Stringer
    Resetter
}

// 一个泛型函数,要求传入的类型满足 StringerAndResetter 接口。
func process[T StringerAndResetter](t T) {
    fmt.Println(t.String())
    t.Reset()
    fmt.Println(t.String())
}

// 自定义类型,实现了 Stringer 和 Resetter 接口。
type MyType struct {
    content string
}

func (m MyType) String() string {
    return m.content
}

func (m *MyType) Reset() {
    m.content = strings.ToUpper(m.content)
}

func main() {
    myValue := MyType{content: "Hello, World!"}
    process(&myValue)
}

在这个例子中,process 函数使用了泛型,其类型参数 T 必须满足 StringerAndResetter 接口。这意味着任何传递给 process 的类型都必须实现 StringReset 方法。这样的复杂类型约束使得泛型函数更加灵活且安全。

相关推荐
欧雷殿18 分钟前
适配一人公司!家庭局域网 AI 工作台来了
后端·agent·aiops
ltl18 分钟前
梯度下降与反向传播
后端
老马952728 分钟前
opencode6-桌面应用实战1
人工智能·后端
掘金者阿豪38 分钟前
NAS搭好了但找不到资源?用Pansou同时搜几十个网盘,帮我省了不少会员钱
后端
第五页的你41 分钟前
Go语言--一篇通
后端
数据仓库搬砖人42 分钟前
DWS 列存表分区创建原理详解
后端
渐儿43 分钟前
上下文工程 · 02 · 工具结果的反注入与信任边界
后端
得物技术44 分钟前
基于 Harness + SDD + 多仓管理模式的 AI 全栈开发实践|得物技术
前端·人工智能·后端
掘金者阿豪1 小时前
服务器突然卡了却找不到原因?cAdvisor让每个容器都透明可见
后端
程序员三明治1 小时前
【AI】Prompt 工程入门:从五要素框架到 RAG 生产级 Prompt 模板与 Java 实战
java·人工智能·后端·大模型·llm·prompt·agent