GO语言学习笔记(6)| 青训营笔记

74 阅读3分钟

[ go 与 golang | 青训营笔记]

这是我参与「第五届青训营 」伴学笔记创作活动的第 6天, 在学习了go的相关基础知识以后,可以说是初步的了解了go,我在这过程中也记录了一些自己的学习经验以及心得,与大家分享一下。

22. 泛型

从1.18版本开始,Go添加了对泛型的支持,即类型参数

泛型函数

没有泛型的痛点

  1. 对于相近类型的操作,可能会写多个一模一样的函数
  2. 不停的类型转换
package main

import "fmt"
// 遍历int切片
func PrintIntSlice(slice []int) {
  for _, v := range slice {
    fmt.Printf("%T  %v\n", v, v)
  }
}
// 遍历int64切片
func PrintInt64Slice(slice []int64) {
  for _, v := range slice {
    fmt.Printf("%T  %v\n", v, v)
  }
}
// int64切片转int切片
func Int64SliceToIntSlice(Int64Slice []int64) (IntSlice []int) {
  for _, v := range Int64Slice {
    IntSlice = append(IntSlice, int(v))
  }
  return
}

func main() {
  PrintIntSlice([]int{1, 2, 3, 4, 5})
  var int64Slice []int64 = []int64{4, 5, 7}
  PrintInt64Slice(int64Slice)

  var intSlice []int
  for _, v := range int64Slice {
    intSlice = append(intSlice, int(v))
  }
  PrintIntSlice(intSlice)

  PrintIntSlice(Int64SliceToIntSlice(int64Slice))

}

有了泛型函数,就很方便

package main

import "fmt"

// 泛型函数
func PrintSliceTypeSlice[T int | int64 | string](slice []T) {
  fmt.Printf("%T\n", slice)
  for _, v := range slice {
    fmt.Printf("%T  %v\n", v, v)
  }
}

func main() {

  PrintSliceTypeSlice([]int{1, 2, 3, 4, 5})
  PrintSliceTypeSlice([]int64{1, 2, 3, 4, 5})
  PrintSliceTypeSlice([]string{"hello"})
  
  // 标准写法
  PrintSliceTypeSlice[int]([]int{1, 2, 3, 4, 5})
  PrintSliceTypeSlice[int64]([]int64{1, 2, 3, 4, 5})
  PrintSliceTypeSlice[string]([]string{"hello"})

}

泛型切片

package main

import "fmt"

type mySlice[T int | string] []T

func printIntTypeSlice[T int | int64 | string](slice []T) {
  fmt.Printf("%T\n", slice)
  for _, v := range slice {
    fmt.Printf("%T  %v\n", v, v)
  }
}
func main() {
  v1 := mySlice[int]{1, 2, 3, 4, 5}
  printIntTypeSlice(v1)
  v2 := mySlice[string]{"hello"}
  printIntTypeSlice(v2)
}

泛型map

package main

import "fmt"

type myMap[K string | int, V any] map[K]V
type _User struct {
  Name string
}

func main() {
  m1 := myMap[string, string]{
    "key": "fengfeng",
  }
  fmt.Println(m1)
  m2 := myMap[int, _User]{
    0: _User{"枫枫"},
  }
  fmt.Println(m2)
}

泛型约束

package main

import "fmt"

type NumStr interface {
  Num | Str
}

// ~的意思就是底层数据类型
type Num interface {
  ~int | ~int32 | ~int64 | ~uint8
}
type Str interface {
  string
}

type Status uint8

type mySlice1[T NumStr] []T

func main() {
  m1 := mySlice1[int]{1, 2, 3}
  fmt.Println(m1)
  m2 := mySlice1[int64]{1, 2, 3}
  fmt.Println(m2)
  m3 := mySlice1[string]{"hello"}
  fmt.Println(m3)
  m4 := mySlice1[Status]{1, 2, 3}
  fmt.Println(m4)
}

约束方法

package main

import (
  "fmt"
  "strconv"
)

type Price int

func (p Price) String() string {
  // int转数字
  return strconv.Itoa(int(p))
}

type Price2 string

func (p Price2) String() string {
  // int转数字
  return string(p)
}

type showPrice interface {
  ~int | ~string
  String() string
}

func showPriceFunc[T showPrice](p T) {
  fmt.Println(p.String())

}

func main() {
  var p1 Price = 12
  showPriceFunc(p1)
  var p2 Price2 = "56"
  showPriceFunc(p2)
}

23. 反射

go语言提供了一种机制,在编译时不知道类型的情况下,可更新变量,在运行时查看值,调用方法以及直接对他们的布局进行操作。这种机制称为反射(reflection)。

为什么使用反射

使用空接口,可以传任意类型的数据,但是不能修改原始值

package main

import "fmt"

type User struct {
  Name string
  Age  int
}

func Print(inter interface{}) {
  switch x := inter.(type) {
  case User:
    x.Name = "张三"
    fmt.Println(x.Name, x.Age)
  }
  //user := inter.(User)
  //user.Name = "xxx"
}

func PPrint(user *User) {
  user.Name = "王五"
}

func main() {
  user := User{"枫枫", 21}
  Print(user)
  fmt.Println(&user)

  //PPrint(&user)
  //fmt.Println(user)
}

用反射就能动态修改原始数据

反射的优缺点

  1. 可以动态修改数据的值
  2. 性能问题
  3. 可读性不太好

reflect包

两个很重要的方法 TypeOf,ValueOf

package main

import (
  "fmt"
  "reflect"
)

type User struct {
  Name string `json:"name" feng:"name_xxx"`
  Age  int    `json:"age" feng:"age_xxx"`
}

func FPrint(inter interface{}) {
  t := reflect.TypeOf(inter)
  v := reflect.ValueOf(inter)
  //fmt.Println(t.Kind()) // 获取这个接口的底层类型
  //fmt.Println(t.Elem()) // 变量的原始类型
  for i := 0; i < t.NumField(); i++ {
    //fmt.Println()
    // 字段的类型
    // 字段名
    // 字段的值
    // 字段的tag
    fmt.Println(
      t.Field(i).Type,
      t.Field(i).Name,
      v.Field(i),
      t.Field(i).Tag.Get("feng"),
    )
  }

}

func main() {
  user := User{"枫枫", 21}
  FPrint(user)
}

修改结构体的数据

package main

import (
  "fmt"
  "reflect"
)

type User struct {
  Name string `json:"name" feng:"name_xxx"`
  Age  int    `json:"age" feng:"age_xxx"`
}

func FPrint(inter interface{}) {
  v := reflect.ValueOf(inter)
  e := v.Elem()  // 必须用这个
  e.FieldByName("Name").SetString("枫枫知道")
}

func main() {
  user := User{"枫枫", 21}
  FPrint(&user)  // 必须传指针
  fmt.Println(user)
}

24. 文件操作

IO包的基础使用

Reader interface

type Reader interface {
  Read(p []byte) (n int, err error)
}

将len(p)个字节读取到p中

Writer interface

type Writer interface {
  Write(p []byte) (n int, err error)
}

用于将p中的数据写入到对象的数据流中