Re:从零开始的 Go 之旅- 基础(4)

本文最后更新于 2025年3月24日 晚上

…ing

接口

接口只定义而不实现

接口类型

Go 中的接口(interface)是一种抽象的类型,interface 是一组方法的集合

1
2
Basic Interface:只要包含方法集的接口
General Interface:只要包含类型集的接口

接口声明

1
2
3
4
5
type 接口类型名 interface{
方法名1( 参数列表1 ) 返回值列表1
方法名2( 参数列表2 ) 返回值列表2

}

一个简单的例子,定义了 Animal 接口,dogcat 结构体,实现 Say() 方法,实现该接口的实例

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
type Animal interface {
Say()
}
type dog struct{}
type cat struct{}

func (d dog) Say() {
fmt.Println("www")
}

func (c cat) Say() {
fmt.Println("mmm")
}

func main() {
var hay Animal
a := cat{}
b := dog{}
hay = a
hay.Say()
hay = b
hay.Say()
}

>>
mmm
www

实现

方法集是接口方法的超集,实现方法不需要 implements 关键字指定实现,只要是实现了一个接口的全部方法,那就是实现了该接口,

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
type Person interface {
Say(string) string
Walk(int)
}

type Man interface {
Exercise()
Person
}
type Number int

func (n Number) Say(s string) string {
return "bibibibibi"
}

func (n Number) Walk(i int) {
fmt.Println("can not walk")
}

type Func func()

func (f Func) Say(s string) string {
f()
return "bibibibibi"
}

func (f Func) Walk(i int) {
f()
fmt.Println("can not walk")
}

func main() {
var function Func
function = func() {
fmt.Println("do somthing")
}
function()
}
>>
do somthing

上面这个例子中,Man 方法是 Person 方法的超集,所以 Man 也实现了接口 Person;类型 NumberintNumber 方法也是 Person 的超集。函数类型也能实现接口。

空接口

空接口是所有类型集的集合,包含所有类型

1
2
3
type Any interface{

}

Any 接口内部没用方法集合,所有类型都是 Any 接口的实现,所有类型的方法都是空集的超集,Any 可以保存任何类型的值

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
42
43
type Any interface {
}

func main() {
var anything Any

anything = 1
println(anything)
fmt.Println(anything)

anything = "sad"
println(anything)
fmt.Println(anything)

anything = complex(1, 2)
println(anything)
fmt.Println(anything)

anything = 1.2
println(anything)
fmt.Println(anything)

anything = []int{}
println(anything)
fmt.Println(anything)

anything = map[string]int{}
println(anything)
fmt.Println(anything)
}
>>
(0x9f23e0,0xa2f968)
1
(0x9f21a0,0xa307e0)
sad
(0x9f24e0,0xa307f0)
(1+2i)
(0x9f2560,0xa30698)
1.2
(0x9f1560,0xc000008030)
[]
(0x9f6480,0xc00001e180)
map[]

两种输出结果不一样,接口内部可以看成由(val,type) 组成的元组,在调用方法的时候会调用具体类型的具体值

1
interface{}

一个匿名空接口,表示接收任何类型的值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
type any = interface{}

func main() {
var a interface{}
var b interface{}
a = 1
b = "1"
fmt.Println(a == b)
a = 1
b = 1
fmt.Println(a == b)
}
>>
false
true

比较空接口时,先比较底层类型,如果类型不匹配的话则为 false,然后再比较值

如果底层类型不能比较会 paniccomparable 代表所有可比较类型

1
type comparable interface{ comparable }

值接收实现接口

值接收者实现接口之后,不管是 dog 结构体还是结构体指针 *dog类 型的变量都可以赋值给该接口变量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
type Mover interface {
move()
}
type dog struct{}

func (d dog) move() {
fmt.Println("run")
}
func main() {
var x Mover
var a = dog{} // a 是 dog 类型
x = a // x 可以接收 dog 类型
var b = &dog{} // b 是 *dog 类型
x = b // x 可以接收 *dog 类型
x.move()
}
>>
run

指针接收实现接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
type Mover interface {
move()
}
type dog struct{}

func (d *dog) move() {
fmt.Println("run")
}
func main() {
var x Mover
//var a = dog{} // a 是 dog 类型
//x = a // x 可以接收 dog 类型
var b = &dog{} // b 是 *dog 类型
x = b // x 可以接收 *dog 类型
x.move()
}
>>
run

Mover 接口的是 *dog 类型,所以不能给 x 传入 dog 类型的 a,此时 x 只能存储 *dog 类型的值。

泛型

1
2
3
4
5
6
7
func Sum[T int | float64](a, b T) T {
return a + b
}

// T 是类型形参,取决于传进来的类型
// int | float64 构成了类型约束,规定了允许的类型
// Sum[int]() 指定 int 类型

显式指明用哪种类型

1
Sum[int](2012, 2022)

不指定类型

1
Sum(1.23,1.14514)

泛型切片,类型约束 int | int32 | int64

1
type GenercSlice[T int | int32 | int64] []T

不能省略类型实参

1
GenercSlice[int]{1,2,3}

泛型哈希表,键的类型必须可比较,使用 comparable 接口,值的类型约束为 V int | string | byte

1
type GenericMap[K comparable,V int | string | byte] map[K]V

使用如下

1
2
3
4
5
6
7
8
9
10
11
12
type GenericMap[K comparable, V int | string | byte] map[K]V

func main() {
var gmap1 = GenericMap[int, string]{1: "sad"}
var gmap2 = make(GenericMap[string, byte], 0)

fmt.Println(gmap1)
fmt.Printf("gmap2: %#v\n", gmap2)
}
>>
map[1:sad]
gmap2: main.GenericMap[string,uint8]{}

泛型结构体,约束类型为 T int | string

1
2
3
4
type GenericStruct[T int | string] struct {
Name string
Id T
}

SayAble 是一个泛型接口,Person 实现了该接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
type SayAble[T int | string] interface {
Say() T
}
type Person[T int | string] struct {
msg T
}
func (p Person[T]) Say() T {
return p.msg
}
func main() {
var s SayAble[string]
s = Person[string]{"sad"}
fmt.Println(s.Say())
}
>> sad

泛型接口

泛型接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
func PrintObj[T fmt.Stringer](s T) {
fmt.Println(s.String())
}

type Person struct {
Name string
}

func (p Person) String() string {
return fmt.Sprintf("Person: %s", p.Name)
}

func main() {
PrintObj(Person{Name: "butt3rf1y"})
}
>>
Person: butt3rf1y

非泛型接口也能作为泛型的类型形参

1
2
3
func Write[W io.Writer](w W, bs []byte) (int, error) {
return w.Write(bs)
}

泛型断言

类型断言是将接口类型的变量转换为具体类型

可以用泛型来对 any 类型进行类型断言

1
2
3
4
5
6
7
8
func Assert[T any](v any) (bool, T) {
var av T
if v == nil {
return false, av
}
av, ok := v.(T)
return ok, av
}

类型集

type set 只能用于泛型中的类型约束,不能做类型声明,类型断言。

并集

接口类型 SignedInt 是一个类型集,有符号整数类型的并集就是 SignedInt ,所以 SignedInt 就是她们的超集

1
2
3
type SignedInt interface {
int8 | int16 | int | int32 | int64
}

其他通用接口也是这样

1
2
3
4
5
6
7
8
9
type SignedInt interface {
int8 | int16 | int32 | int64
}
type UnSignedInt interface {
int8 | int16 | int32 | int64
}
type Integer interface {
SignedInt | UnSignedInt
}

交集

显而易见交集为 SignedInt,并且因为 Do[uint](2) 类型为 uint 所以无法通过编译,只有交集中才能通过编译

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
type SignedInt interface {
int8 | int16 | int | int32 | int64
}
type Integer interface {
int8 | int16 | int | int32 | int64 | uint8 | uint16 | uint | uint32 | uint64
}
type Number interface {
SignedInt
Integer
}

func Do[T Number](n T) T {
return n
}
func main() {
Do[int](2)
Do[uint](2)
}

空集

像下面这样,空集就是没有交集

1
2
3
4
5
6
7
8
9
10
type SignedInt interface {
int8 | int16 | int | int32 | int64
}
type UnsignedInt interface {
uint8 | uint16 | uint | uint32 | uint64
}
type Integer interface {
SignedInt
UnsignedInt
}

底层类型

type 关键字声明新类型时,其底层类型即使包含在类型集内,传入时也不能通过编译

1
2
3
4
5
6
7
8
9
10
11
type Int interface {
int8 | int16 | int | int32 | int64 | uint8 | int16 | uint | uint32 | uint64
}
type TinyInt int8

func Do[T Int](n T) {
return n
}
func main() {
Do[TinyInt](1)
}

在类型前加上 ~ 表示底层类型,如果一个类型的底层类型属于该类型集,那么该类型就属于类型集。这样就能通过编译了

1
2
3
4
5
6
7
8
9
10
11
type Int interface {
~int8 | ~int16 | ~int | ~int32 | ~int64 | ~uint8 | ~uint16 | ~uint | ~uint32 | ~uint64
}
type TinyInt int8

func Do[T Int](n T) T {
return n
}
func main() {
Do[TinyInt](1)
}

泛型的使用

队列

队列中的元素是任意的,所以类型约束为 any

1
type Queue[T any] []T

总共有四个方法 PopPeekPushSize

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
type Queen[T any] []T

func (q *Queen[T]) Push(e T) {
*q = append(*q, e)
}

func (q *Queen[T]) Pop(e T) (_ T) {
if q.Size() > 0 {
res := q.Peek()
*q = (*q)[1:]
return res
}
return
}

func (q *Queen[T]) Peek() (_ T) {
if q.Size() > 0 {
return (*q)[0]
}
return
}

func (q *Queen[T]) Size() int {
return len(*q)
}

PopPeek 方法中返回值是 _T ,表示泛型零值,当队列为空时,需要返回零值,但不知道类型不能返回具体类型,所以返回泛型零值。对于泛型变量,默认的值就是该类型的零值

堆的元素必须是可排序的类型,内置的可排序类型只有数字和字符串,初始化时需要传入一个自定义的比较器,比较器必须使用泛型

1
type Comparator[T any] func(a, b T) int

对象池

复用 *bytes.Buffer 对象

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
42
43
44
45
46
47
48
49
50
51
52
53
54
package main

import (
"bytes"
"fmt"
"sync"
)

func NewPool[T any](newFn func() T) *Pool[T] {
return &Pool[T]{
pool: &sync.Pool{
New: func() interface{} {
return newFn()
},
},
}
}

type Pool[T any] struct {
pool *sync.Pool
}

func (p *Pool[T]) Put(v T) {
p.pool.Put(v)
}

func (p *Pool[T]) Get() T {
var v T
get := p.pool.Get()
if get != nil {
v, _ = get.(T)
}
return v
}

func main() {
bufferPool := NewPool(func() *bytes.Buffer {
return bytes.NewBuffer(nil)
})

for range 5 {
buffer := bufferPool.Get()
buffer.WriteString("sad")
fmt.Println(buffer.String())
buffer.Reset()
bufferPool.Put(buffer)
}
}
>>
sad
sad
sad
sad
sad

Re:从零开始的 Go 之旅- 基础(4)
http://example.com/2025/03/23/Go-4/
作者
butt3rf1y
发布于
2025年3月23日
许可协议