likes
comments
collection
share

泛型、泛型函数、结构体、泛型结构体

作者站长头像
站长
· 阅读数 69

泛型

泛型是计算机编程语言中的一个概念,它允许我们在编写代码时不特定于单一数据类型的数据结构或函数。我们可以使用变量来代表数据结构。在代码实际运行时,通过编辑器或运行环境来确定实际的数据类型

  • 定义:在函数名或数据类型后使用[类型参数 约束]
    • 类型参数:在我们定义泛型时,可以使用一个或多个类型参数代表实际类型
    • 约束:用来限制类型参数的数据类型

使用:定义一个可以表示整型和字符串型这两种类型的泛型切片

type MySlice[T int | string] []T

slice1 := MySlice[int]{1, 2, 3}
slice2 := MySlice[string]{"1", "2", "3"}

fmt.Println(slice1, slice2)

注意:泛型不能够同时表示多种基本数据类型(如:int、string、bool)

type intString[T int | string] T // 报错: Cannot use a type parameter as RHS in type declaration

上述代码报错: Cannot use a type parameter as RHS(Right-Hand Side) in type declaration

泛型函数

  • 定义:在函数名后使用方括号[类型参数 约束]
    func plus[T int | string] (i T) T {}
  • 作用:提高代码的复用性

普通函数:

package main

import "fmt"

func main() {
    resInt := plusInt(1, 2)
    resFloat := plusFloat(1.2, 2.2)
    resString := plusString("你", "好")

    fmt.Println(resInt, resFloat, resString) // 3 3.4000000000000004 你好
}

func plusInt(a, b int) int {
    return a + b
}

func plusFloat(a, b float64) float64 {
    return a + b
}

func plusString(a, b string) string {
    return a + b
}

打印结果: 3 3.4000000000000004 你好 我们可以看到函数plusInt()、plusFloat()、plusString()这三个函数具有相同的参数形式,返回值、函数体。但由于参数类型不同导致写了三个不同的函数来完成相应的功能。

泛型函数:

package main

import "fmt"

func main() {
    /**
    定义一个泛型函数
    */
    resInt := plus(1, 2)
    resFloat := plus(1.1, 2.2)
    resString := plus("你", "好")

    fmt.Println(resInt, resFloat, resString) // 3 3.3000000000000003 你好
}

func plus[T int | string | float64](a, b T) T {
    return a + b
}

打印结果: 3 3.3000000000000003 你好 上述案例中我们使用[T int | falot64 | string]定义了一个泛型参数T,然后在函数参数和返回值类型中使用了这个T

  • 除了使用|来定义多种数据类型,我们还可以使用any来表示所有类型。[T any]
package main

import "fmt"

func main() {
    //newSlice := []int{1, 2, 3}
    teacherMap := map[string]interface{}{
       "name": "张三",
       "age":  28,
    }

    studentMap := map[string]interface{}{
       "name": "李四",
       "age":  18,
    }

    mySlice := make([]map[string]interface{}, 2)
    mySlice = append(mySlice, teacherMap, studentMap)

    printSlice(mySlice)
}

func printSlice[T any](E []T) {
    for _, val := range E {
       fmt.Println(val)
    }
}

打印结果: map[] map[] map[age:28 name:张三] map[age:18 name:李四]

  • any外,还有一个特殊的类型约束字段comparable用来比较数据是否相等。[T comparable]
func equals[T comparable](a, b T) bool {
    return a == b
}
  • 注意:slice、map等数据类型不能作为比较
package main

import "fmt"

func main() {
    myMap1 := map[string]string{
       "name": "18",
    }
    myMap2 := map[string]string{
       "name": "18",
    }
    res1 := equals("a", "a")
    res2 := equals(1, 2)

    fmt.Println(res1, res2)        // true false
    res3 := equals(myMap1, myMap2) // 报错: map[string]string does not satisfy comparable

    fmt.Println(res1, res2, res3)
}

打印结果: true false map[string]string does not satisfy comparable

结构体(struct)

前言: Go泛型的引用是在1.18版本开始的,1.18以下版本没有泛型。

  • 定义:
type MyStudent struct {
    id        int
    name      string
    content   string
}
  • 使用:
package main

import "fmt"

func main() {
    type MyStudent struct {
       id      int
       name    string
       content string
    }
    // 使用字段名进行赋值,不需要进行一一对应
    student := MyStudent{
       id:   1,
       name: "李四",
    }
    // 直接进行赋值,需要一一对应
    student2 := MyStudent{
       1,
       "张三",
       "好好学习、天天向上",
    }

    fmt.Println(student, student2) // {1 李四 } {1 张三 好好学习、天天向上}
}

打印结果: {1 李四 } {1 张三 好好学习、天天向上}} 注意:对结构体类型进行赋值有两种方法 1.字段名对应字段值。2. 直接进行赋值。前者不需要对全部字段进行赋值,后者需按字段名依次进行赋值。

匿名结构体

使用场景:想临时使用一个结构体,不需要使用type关键字声明一个结构体类型。

package main 

import "fmt"

person := struct {
    name   string
    age    int
}{
    name: "Sun",
    age : 22,
}

fmt.Println (person) // {Sun 22}

打印结果:{Sun 22}

嵌套结构体

结构体内部还可以嵌套结构体,也就是套娃。

func main () {
    type LogisticsInfo struct {
        orderId      int
        productName  string
        address      struct {
            province   string
            city       string
            district   string
            detail     string
        }
    }
    // 键名键值初始化
    logisticsInfo := LogisticsInfo{
        orderId:     1,
        productName: "北京",
        address: struct {
           province string
           city     string
           district string
           detail   string
        }{
           province: "北京",
           city:     "北京",
           district: "xxx",
           detail:   "xxx",
        },
    }
    fmt.Println(logisticsInfo) // {1 北京 {北京 北京 xxx xxx}}
    // 匿名初始化
    logisticsInfo2 := LogisticsInfo{
        2,
        "上海",
        struct {
           province string
           city     string
           district string
           detail   string
        }{
           "上海",
           "上海",
           "xxx",
           "xxx",
        },
    }
    fmt.Println(logisticsInfo2) // {2 上海 {上海 上海 xxx xxx}}
    }

我们也可以在外部声明一个结构体嵌套在结构体内部

 type Address struct {
     province   string
     city       string
     district   string
     detail     string
}

type LogisticsInfo struct {
    orderId      int
    productName  string
    address      Address
}
func main () {
     // 键名键值初始化
    logisticsInfo := LogisticsInfo{
        orderId:     1,
        productName: "北京",
        address: Address{
           province: "北京",
           city:     "北京",
           district: "xxx",
           detail:   "xxx",
        },
    }
    fmt.Println(logisticsInfo) // {1 北京 {北京 北京 xxx xxx}}
    // 匿名初始化
    logisticsInfo2 := LogisticsInfo{
        2,
        "上海",
        Address{
           "上海",
           "上海",
           "xxx",
           "xxx",
        },
    }
    fmt.Println(logisticsInfo2) // {2 上海 {上海 上海 xxx xxx}}
}

结构体绑定函数

前言:结构体绑定函数必须又有一个接受者(接受一个结构体) 结构体是值引用类型,因此当结构体作为参数传递时,Go会将它的值copy一份进行传递,也就是值传递,并不会修改它原始值。 如:

package main

import "fmt"

type Todo struct {
    id        int
    content   string
    completed bool
}

func main() {
    todo := Todo{
       id:        1,
       content:   "This is todo",
       completed: true,
    }
    // 获取todo的地址
    fmt.Printf("%p\r\n", &todo)
    todo.setId(11)
    todo.setContent("HUAWEI META60")
    todo.setCompleted(false)

    fmt.Println(todo)
}

func (todo Todo) setId(id int) int {
    // 获取todo的地址
    fmt.Printf("%p\r\n", &todo)
    todo.id = id
    return id
}

func (todo Todo) setContent(content string) string {
    todo.content = content
    return content
}

func (todo Todo) setCompleted(completed bool) bool {
    todo.completed = completed
    return completed
}

打印结果:0x140000ac000 0x140000ac020 {1 This is todo true} todo变量的值没有发生改变,说明只是将值进行拷贝传递。

那么如何改变其原始值呢?使用指针接收

package main

import "fmt"

type Todo struct {
    id        int
    content   string
    completed bool
}

func main() {
    todo := Todo{
       id:        1,
       content:   "This is todo",
       completed: true,
    }
    // 获取todo变量的的地址
    fmt.Printf("%p\r\n", &todo)
    todo.setId(11)
    todo.setContent("HUAWEI META60")
    todo.setCompleted(false)

    fmt.Println(todo)
}

func (todo *Todo) setId(id int) int {
    // 获取todo指针变量指向的地址
    fmt.Printf("%p\r\n", todo)
    todo.id = id
    return id
}

func (todo *Todo) setContent(content string) string {
    todo.content = content
    return content
}

func (todo *Todo) setCompleted(completed bool) bool {
    todo.completed = completed
    return completed
}

打印结果:0x140000ac000 0x140000ac000 {11 HUAWEI META60 false}

泛型结构体

和泛型函数的使用差不多,也是在变量名后加方括号[类型参数 约束]

  • 定义
type Todo[T string | int] struct {
    id        int
    content   T
    completed bool
}

上述定义了一个泛型T类型约束为stringint类型

  • 使用泛型结构体Todo
package main

import "fmt"

type Todo[T string | int] struct {
    id        int
    content   T
    completed bool
}

func main() {
    todo := Todo[string]{
       id:        1,
       content:   "This is todo",
       completed: true,
    }

    fmt.Println(todo)
}
  • 定义泛型参数T,约束为[]stringstring
type Todo[T []string | string] struct {
    id        int
    content   T
    completed bool
}
  • 使用
package main

import "fmt"

type Todo[T []string | string] struct {
    id        int
    content   T
    completed bool
}

func main() {
    todoList1 := make([]Todo[string], 0)
    todoList1 = append(todoList1, Todo[string]{
       id:        1,
       content:   "todo1",
       completed: false,
    })

    todoList2 := make([]Todo[[]string], 0)
    todoList2 = append(todoList2, Todo[[]string]{
       id:        2,
       content:   []string{"todo2", "todo3"},
       completed: true,
    })

    fmt.Println(todoList1)
    fmt.Println(todoList2)
}

打印结果:[{1 todo1 false}] [{2 [todo2 todo3] true}]

转载自:https://juejin.cn/post/7294103091020431412
评论
请登录