GO - 泛型编程

go - 泛型编程

介绍

泛型即开发过程中编写适用于所有类型的模板,只有在具体使用的时候才能确定其真正的类型。随着Go 1.18版本的发布,泛型正式成为了Go语言的一部分。
在编写代码时,我们经常会遇到需要处理不同类型的数据的情况。传统上,我们需要为每种类型编写不同的函数或数据结构,这导致了代码的重复和冗余。而泛型编程可以解决这个问题,使得我们可以编写通用的代码,适用于多种类型的数据。

泛型作用

泛型编程有许多优势,包括提高代码的可读性、可维护性和可扩展性,减少代码的重复和冗余,以及增加代码的灵活性。然而,泛型编程也有一些局限性,包括可能引入额外的复杂性,增加编译时间,以及不适用于所有情况。

golang的泛型怎么使用

泛型函数

泛型函数是可以接受不同类型参数的函数。在定义泛型函数时,我们使用类型参数来代表参数类型,从而实现函数的通用性。
使用:在 Go 语言中,我们使用 func 关键字来定义函数,泛型函数的定义与普通函数类似,只是在函数名后面添加了类型参数的声明。泛型函数的类型参数可以是任意类型,通常使用大写字母开头的单个字母来表示。
特化:当我们调用泛型函数时,编译器会根据传入参数的类型特化生成相应的函数。例如,当我们将一个 []int 类型的切片传入 Print 函数时,编译器会生成一个专门用于处理 int 类型切片的函数;同样地,当传入一个 []string 类型的切片时,会生成一个专门用于处理 string 类型切片的函数。

package main

import "fmt"

func Print[T any](s []T) {
    for _, v := range s {
        fmt.Println(v)
    }
}

func main() {
    s := []int{1, 2, 3, 4, 5}
    Print(s)
    
    s2 := []string{"hello", "world"}
    Print(s2)
}

泛型类型

泛型类型是一种可以适用于多种类型数据的数据结构或容器。与普通类型不同,泛型类型中的某些部分使用类型参数来代表任意类型的数据。通过使用类型参数,我们可以定义一次通用的数据结构或容器,而不必针对每种类型都编写单独的实现。
使用:在 Go 语言中,泛型类型通常通过在类型名称后面添加类型参数列表来定义。类型参数列表使用方括号括起来,并在其中声明一个或多个类型参数。

type Stack[T any] struct {
    elements []T
}

Stack 是一个泛型类型,它接受一个类型参数 T,表示栈中存储的元素类型。通过使用类型参数 T,我们可以定义一个通用的栈数据结构,使得栈可以存储任意类型的元素。
泛型类型的定义之后,我们可以使用具体的类型来实例化泛型类型。例如,我们可以使用 Stack[int] 来表示一个整数类型的栈,使用 Stack[string] 来表示一个字符串类型的栈,以此类推。

stack := Stack[int]{}

package main

import "fmt"

type Stack[T any] struct {
    elements []T
}

func (s *Stack[T]) Push(e T) {
    s.elements = append(s.elements, e)
}

func (s *Stack[T]) Pop() T {
    if len(s.elements) == 0 {
        return nil
    }
    e := s.elements[len(s.elements)-1]
    s.elements = s.elements[:len(s.elements)-1]
    return e
}

func main() {
    stack := Stack[int]{}
    stack.Push(1)
    stack.Push(2)
    stack.Push(3)
    
    fmt.Println(stack.Pop()) // Output: 3
    fmt.Println(stack.Pop()) // Output: 2
    fmt.Println(stack.Pop()) // Output: 1
}

泛型接口

泛型接口是一种可以接受任意类型参数的接口,它们使用类型参数来代表任意类型的数据。通过使用类型参数,我们可以定义一次通用的接口,而不必针对每种类型都编写单独的接口。这样,我们就能够针对不同类型的数据实现相同的接口方法,实现了对不同类型数据的通用操作。
使用 :泛型接口通常通过在接口名称后面添加类型参数列表来定义。类型参数列表使用方括号括起来,并在其中声明一个或多个类型参数。

type Container[T any] interface {
    Add(T)
    Remove() T
}

Container 是一个泛型接口,它接受一个类型参数 T,表示容器中存储的元素类型。通过使用类型参数 T,我们可以定义一个通用的容器接口,使得容器可以存储任意类型的元素,并且具有相同的添加和移除元素的方法。
泛型接口的定义之后,我们可以使用具体的类型来实现泛型接口。例如,我们可以针对不同类型的数据分别实现 Container 接口的方法:

type Queue[T any] struct {
    elements []T
}

func (q *Queue[T]) Add(e T) {
    q.elements = append(q.elements, e)
}

func (q *Queue[T]) Remove() T {
    if len(q.elements) == 0 {
        return nil
    }
    e := q.elements[0]
    q.elements = q.elements[1:]
    return e
}

我们针对不同类型的数据实现了 Container 接口的方法,分别用于实现一个泛型队列数据结构。通过实现泛型接口,我们可以实现对不同类型数据的通用操作,提高了代码的复用性和灵活性。

泛型结构体

泛型结构体是一种可以适用于多种类型数据的结构体,它们使用类型参数来代表任意类型的数据。通过使用类型参数,我们可以定义一次通用的结构体,而不必针对每种类型都编写单独的结构体。这样,我们就能够定义一个通用的数据结构,用于存储任意类型的数据。
使用:泛型结构体通常通过在结构体名称后面添加类型参数列表来定义。类型参数列表使用方括号括起来,并在其中声明一个或多个类型参数。例如:

type Pair[T any] struct {
    First  T
    Second T
}

Pair 是一个泛型结构体,它接受一个类型参数 T,表示结构体中存储的元素类型。通过使用类型参数 T,我们可以定义一个通用的结构体,使得结构体中的字段可以存储任意类型的数据,并且具有相同的结构。
泛型结构体的定义之后,我们可以使用具体的类型来实例化泛型结构体。例如:

pair1 := Pair[int]{First: 1, Second: 2}
pair2 := Pair[string]{First: "hello", Second: "world"}

在上面的示例中,我们分别使用 Pair[int] 和 Pair[string] 来实例化了两个不同类型的泛型结构体。通过实例化泛型结构体,我们可以创建不同类型的数据结构,用于存储不同类型的数据。

泛型receiver

泛型 receiver 是指在方法定义中使用类型参数来表示接收者类型的一种方式。通过使用类型参数,可以使得方法适用于多种类型的接收者,从而实现对不同类型的数据的通用操作。
使用:在 Go 语言中,可以在方法定义中使用类型参数来表示接收者类型。例如:

type Stack[T any] struct {
    elements []T
}

func (s *Stack[T]) Push(e T) {
    s.elements = append(s.elements, e)
}

func (s *Stack[T]) Pop() T {
    if len(s.elements) == 0 {
        return nil
    }
    e := s.elements[len(s.elements)-1]
    s.elements = s.elements[:len(s.elements)-1]
    return e
}

Stack 结构体定义了一个泛型的栈数据结构,然后在方法定义中使用类型参数 T 来表示接收者类型。这样,Push 和 Pop 方法就可以适用于不同类型的栈,而不必针对每种类型都编写单独的方法。
下面是一个使用泛型 receiver 的示例,用于实现一个泛型栈数据结构:

package main

import "fmt"

type Stack[T any] struct {
    elements []T
}

func (s *Stack[T]) Push(e T) {
    s.elements = append(s.elements, e)
}

func (s *Stack[T]) Pop() T {
    if len(s.elements) == 0 {
        return nil
    }
    e := s.elements[len(s.elements)-1]
    s.elements = s.elements[:len(s.elements)-1]
    return e
}

func main() {
    stack := Stack[int]{}
    stack.Push(1)
    stack.Push(2)
    stack.Push(3)
    
    fmt.Println(stack.Pop()) // Output: 3
    fmt.Println(stack.Pop()) // Output: 2
    fmt.Println(stack.Pop()) // Output: 1
}

泛型限制

匿名结构体与匿名函数不支持泛型

目前在 Go 语言中,匿名结构体和匿名函数不支持泛型。这意味着无法在匿名结构体或匿名函数中使用类型参数。

不支持类型断言

类型断言是一种在 Go 中用于判断接口值的实际类型的机制。然而,目前的泛型实现不支持类型断言。因此,在泛型代码中无法使用类型断言来判断类型。

不支持泛型方法,只能通过receiver来实现方法的泛型处理

目前,Go 语言的泛型实现中不支持直接在方法中使用类型参数。虽然可以通过在方法定义中使用类型参数来表示接收者类型(即泛型 receiver),但是不能直接在方法体内部使用类型参数。

~后的类型必须为基本类型,不能为接口类型

在使用泛型类型参数时,其类型必须为基本类型,不能为接口类型。这意味着类型参数不能用于定义接口类型,而只能用于定义基本类型的变量或参数。

相关推荐

  1. GO - 编程

    2024-04-08 23:12:03       13 阅读
  2. Go专家编程——

    2024-04-08 23:12:03       5 阅读
  3. GO——

    2024-04-08 23:12:03       4 阅读
  4. C++ 编程 模板

    2024-04-08 23:12:03       19 阅读
  5. 如何在Go中使用

    2024-04-08 23:12:03       40 阅读
  6. 一文了解Go

    2024-04-08 23:12:03       34 阅读

最近更新

  1. TCP协议是安全的吗?

    2024-04-08 23:12:03       18 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2024-04-08 23:12:03       19 阅读
  3. 【Python教程】压缩PDF文件大小

    2024-04-08 23:12:03       19 阅读
  4. 通过文章id递归查询所有评论(xml)

    2024-04-08 23:12:03       20 阅读

热门阅读

  1. 面向对象设计之开闭原则

    2024-04-08 23:12:03       22 阅读
  2. ComfyUI是什么?

    2024-04-08 23:12:03       14 阅读
  3. 前端开发语言有哪些?

    2024-04-08 23:12:03       15 阅读
  4. ML Olympiad returns with over 20 challenges

    2024-04-08 23:12:03       15 阅读
  5. 224.0.0.1到224.0.0.9的IP地址

    2024-04-08 23:12:03       15 阅读
  6. 题目 3158: 三国游戏

    2024-04-08 23:12:03       14 阅读
  7. Azure AI 新发布了 9 种更逼真的对话 AI 声音

    2024-04-08 23:12:03       16 阅读
  8. 动态规划(2)

    2024-04-08 23:12:03       14 阅读