Golang上手小记:进阶篇

更多的结构体

在Golang中,数组的长度是不能改变的,为了更灵活地适配更多需求,Golang设计了一种名为Slice的数据结构,用于实现对动态数组的抽象:

1
2
3
4
complex_2 := Complex{1, 3}
complex_3 := Complex{1, 4}
complex_list := [2]Complex{complex_2, complex_3}
complex_slice := []Complex{complex_2, complex_3}

接口

我很久之前就听说过Golang有接口,当时还很意外——一个非OOP的语言,为什么会有接口?稍微学习了一点,写出了一点代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
package main

import (
"fmt"
)

type Lamp interface {
On() string
Off() string
GetStatus() bool
}

type TableLamp struct {
IsOn bool
Information string
}

func (tablelamp *TableLamp) On() string {
tablelamp.IsOn = true
return tablelamp.Information + " ON"
}

func (tableLamp *TableLamp) Off() string {
tableLamp.IsOn = false
return tableLamp.Information + " OFF"
}

func (tableLamp TableLamp) GetStatus() bool {
return tableLamp.IsOn
}

func main() {
tableLamp := TableLamp{
Information: "Table Lamp Switches",
IsOn: false,
}
fmt.Println(tableLamp.On())
fmt.Println(tableLamp.GetStatus())
fmt.Println(tableLamp.Off())
fmt.Println(tableLamp.GetStatus())
}

Golang的所谓接口,在我看来就是模仿OOP的设计,其含义为规定了一类范畴的结构体,必须要实现的方法。

不过,很有意思的是,Golang引入了一个很神奇的东西——空接口,这个东西可以承载任意类型的参数:

1
2
3
4
5
6
7
func main() {
var i interface{}
i = 1
fmt.Println(i)
i = "This is a string"
fmt.Println(i)
}

对我来说,空接口干的事其实是实现了一个类型声明——any。在Golang1.18版本中,也的确引入了这个关键字,官方解释是,any和空接口是一样的,所以这两种写法是一样的:

1
2
3
4
5
6
7
func main() {
var i any
i = 1
fmt.Println(i)
i = "This is a string"
fmt.Println(i)
}

泛型

泛型是我认为最有趣的一个功能,一个代码实例如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
package main

import (
"fmt"
)

type EnableString interface {
ToString() string
}

type BasicString struct {
str string
}

func (bs BasicString) ToString() string {
return bs.str
}

func CompareStringLength[T EnableString](a, b T) bool {
return len(a.ToString()) > len(b.ToString())
}

func main() {
bs1 := BasicString{str: "Hello"}
bs2 := BasicString{str: "Exactly"}

fmt.Println(CompareStringLength(bs1, bs2))
}

Golang中的泛型是极其灵活的,可以通过实现一个接口自定义泛型的约束,只要一个结构体实现了这个约束,就可以应用泛型函数进行处理。通过泛型我们可以实现很多有趣的功能,比如去重功能:

1
2
3
4
5
6
7
8
9
10
11
func FindDuplicatedElement[T comparable](a []T) []T {
result := []T{}
for index, element := range a {
for _, element2 := range a[index+1:] {
if element == element2 {
result = append(result, element)
}
}
}
return result
}

学习到这里,我已经很喜欢Golang这门语言了。

并发

众所周知,b站的一部分开发是依靠Golang完成的,那么我先入为主地认为,Golang的在处理并发场景下也是不错的。在Golang里,创建一个新的线程其实非常简单,一个代码的例子如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
package main

import (
"fmt"
"time"
)

func task1() {
for i := 0; i < 3; i++ {
fmt.Println("This is task1 executing...")
}
time.Sleep(100 * time.Millisecond)
}

func task2() {
for i := 0; i < 3; i++ {
fmt.Println("This is task2 executing...")
}
time.Sleep(100 * time.Millisecond)
}

func main() {
go task1()
go task2()

for i := 0; i < 3; i++ {
fmt.Println("This is main executing...")
time.Sleep(100 * time.Millisecond)
}
}

只需要go关键字,就可以创建一个运行某个函数的新线程。至于通道等机制,我打算通过写实际的项目再来学习。

继承

Golang虽然不是一个OOP语言,但是也在一定程度上实现了继承的功能,只不过,和OOP语言相比,Golang的继承更像是一个包含关系,而非严格意义上的派生关系,比如一个来自菜鸟教程的例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
package main

import "fmt"

// 定义接口
type Speaker interface {
Speak()
}

// 父结构体
type Animal struct {
Name string
}

// 实现接口方法
func (a *Animal) Speak() {
fmt.Println(a.Name, "says hello!")
}

// 子结构体
type Dog struct {
Animal
Breed string
}

func main() {
var speaker Speaker

dog := Dog{
Animal: Animal{Name: "Buddy"},
Breed: "Golden Retriever",
}

speaker = &dog
speaker.Speak() // 通过接口调用方法
}

子结构体通过包含父结构体,实现了所谓的继承操作,除此之外,方法的继承也是在这个包含关系基础上的。

总结

通过加起来一天的上手,Golang具体是怎么一门语言已经十分清晰了,下一步我打算用Golang重写之前用C或者Java等语言实现的一些玩具项目,进一步熟悉Golang本身。

目前为止,我非常喜欢Golang这门语言,因为我所接触的场景大多不是性能优先的场景,所以不需要极致的高性能。Golang或许是我的最佳选择。

文章作者:
文章链接: https://www.coderlock.site/2025/11/10/Golang上手小记:进阶篇/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 寒夜雨