go 的结构内嵌
注意点,有点像js
func main() {
fmt.Println("hello zhangbuda...")
// 这个内嵌 和 js 有点像
co := container{
base: base{
num: 22,
},
str: "zhangdbau hahahahah ",
}
fmt.Println("co: ", co)
/*
在 Go 语言中,如果一个类型嵌入了另一个类型,那么嵌入类型的方法就会被自动提升到外部类型,这就意味着你可以直接在外部类型上调用嵌入类型的方法。
假设 co 是一个结构体,它嵌入了 base 类型,而 base 类型有一个 describe 方法。那么你可以直接在 co 上调用 describe 方法,就像这样:co.describe()。这个调用会自动转发到 base.describe 方法。
所以,co.describe() 和 co.base.describe() 的结果是一致的,因为它们实际上调用的是同一个方法。
但是,如果 co 自己也定义了一个 describe 方法,那么 co.describe() 就会调用 co 的 describe 方法,而不是 base.describe 方法。这是因为 Go 语言会优先调用最近的方法,而不是最匹配的方法。
*/
fmt.Println("co.describe: ", co.describe())
fmt.Println("co.base.describe: ", co.base.describe())
fmt.Println("co.base.num", co.base.num)
fmt.Println("co.str: ", co.str)
}
type base struct {
num int
}
func (b base) describe() string {
// %v 格式化字符串
// %v 会根据值的类型输出对应的值
return fmt.Sprintf("base with num=%v", b.num)
}
type container struct {
base
str string
}
co: {{22} zhangdbau hahahahah }
co.describe: base with num=22
co.base.describe: base with num=22
co.base.num 22
co.str: zhangdbau hahahahah
范型 是重点
package main
import (
"fmt"
)
func main() {
// 范型
// 在 Go 语言中,如果一个类型嵌入了另一个类型,那么嵌入类型的方法就会被自动提升到外部类型,这就意味着你可以直接在外部类型上调用嵌入类型的方法。
var m = map[int]string{1: "2", 2: "4", 4: "8"}
fmt.Println("map: ", m)
fmt.Println("keys: ", MapKeys(m))
//隐式类型转换
s1 := MapKeys(m)
//显式类型转换
s := MapKeys[string, string](map[string]string{"11": "111", "22": "222", "33": "333"})
s2 := MapKeys[int, string](m)
fmt.Println("s1: ", s1, "\ns: ", s, "\ns2: ", s2)
lst := List[int]{}
lst.push(10)
lst.push(13)
lst.push(23)
fmt.Println("list: GetAll:", lst.GetAll())
}
/*
K comparable 表示 K 必须是一个可比较的类型。这是因为在 Go 语言中,map 的键必须是可比较的。
这样,Go 语言才能正确地查找和比较 map 中的键
*/
func MapKeys[K comparable, V any](m map[K]V) []K {
r := make([]K, 0, len(m))
for k := range m {
r = append(r, k)
}
return r
}
// 范型 链表元素
type element[T any] struct {
next *element[T]
val T
}
// 链表结构 包含 头和尾
type List[T any] struct {
head, tail *element[T]
}
func (lst *List[T]) push(v T) {
if lst.tail == nil {
//最开始初始化 链表 头部和尾部都为空
lst.head = &element[T]{val: v}
lst.tail = lst.head
} else {
// 当链表不为空时,便开始把所有元素一次插入尾部,并更新尾部
lst.tail.next = &element[T]{val: v}
lst.tail = lst.tail.next
}
}
func (lst *List[T]) GetAll() []T {
// 定义一个空的切片
var elemes []T
for e := lst.head; e != nil; e = e.next {
elemes = append(elemes, e.val)
}
return elemes
}
/*
再次补充 切片知识
在 Go 语言中,切片(Slice)是对数组的抽象。
Go 数组的长度不可改变,在特定的场景中这样的集合就不太适用。
Go 中提供了一种灵活,功能强大的内置类型切片("动态数组"),与数组相比切片的长度是不固定的,可以追加元素,在追加时可能使切片的容量增大。
切片是一种方便、灵活且强大的包装器,使得对数组操作更加方便。
例如: var number []int
var str []string
*/
map: map[1:2 2:4 4:8]
keys: [1 2 4]
s1: [1 2 4]
s: [11 22 33]
s2: [1 2 4]
list: GetAll: [10 13 23]
package main
import (
"fmt"
"time"
)
func f(from string) {
for i := 0; i < 3; i++ {
fmt.Println(from, ":", i)
}
}
func main() {
f("direct-->")
go f("goroutine-->start")
go func(msg string) {
fmt.Println(msg)
}("going--->")
time.Sleep(time.Second)
fmt.Println("done--->end")
}
# 运行结果
direct--> : 0
direct--> : 1
direct--> : 2
going--->
goroutine-->start : 0
goroutine-->start : 1
goroutine-->start : 2
done--->end
go 的并发 还挺方便的
func hello(i int) {
defer wg.Done()
fmt.Println("hello", i)
}
// sync.WaitGroup 是Go 的一个同步原语,常常用于等待一组goroutine 完成
var wg sync.WaitGroup
func main() {
//go f("hello world")
// fmt.Println("\n===>main function===<\n")
// time.Sleep(time.Second)
for i := 1; i <= 10; i++ {
wg.Add(1)
go hello(i)
}
wg.Wait()
}
#运行结果
hello 10
hello 1
hello 2
hello 3
hello 4
hello 5
hello 6
hello 7
hello 8
hello 9