[ go 与 golang | 青训营笔记]
这是我参与「第五届青训营 」伴学笔记创作活动的第 6天, 在学习了go的相关基础知识以后,可以说是初步的了解了go,我在这过程中也记录了一些自己的学习经验以及心得,与大家分享一下。
22. 泛型
从1.18版本开始,Go添加了对泛型的支持,即类型参数
泛型函数
没有泛型的痛点
- 对于相近类型的操作,可能会写多个一模一样的函数
- 不停的类型转换
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)
}
用反射就能动态修改原始数据
反射的优缺点
- 可以动态修改数据的值
- 性能问题
- 可读性不太好
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中的数据写入到对象的数据流中