15、接口-GOLANG

概念

圆珠笔是一个确切的用于写字的物品,而能够用于写字的物件是一个抽象概念,其中不只有圆珠笔这一个物品,代码中的int,string类型是一个确切的类型,而接口则表达了一种抽象的概念

int类型用于存储整数,string类型用于存储文本,他们非常关心自己存储了什么

接口不关心存储的东西,接口只关心能够做什么

接口类型

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
package main

import "fmt"

var t interface {
talk() string //任何类型只要它声明的talk方法不接受参数并且返回字符串,那么他就可以作为接口t的值
}

type martian struct{}
type laser int

func (m martian) talk() string {
return "martian"
}
func (l laser) talk() string {
return "laser"
}
func main() {
ma := martian{}
t = ma
fmt.Println(t.talk())
var la laser
t = la
fmt.Println(t.talk())

}

为了便于复用,我们一般会把接口声明为类型为其命名,按照惯例,接口类型的名称通常以er作为后缀

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"
"strings"
)

type talker interface {
talk() string
}
type martian struct{}
type laser int

func shout(t talker) {
louder := strings.ToUpper(t.talk()) //strings.ToUpper,将字符串修改为大写格式
fmt.Println(louder)
}
func (m martian) talk() string {
return "martian"
}
func (l laser) talk() string {
return "laser"
}
func main() {

shout(martian{})
var la laser
shout(la)

}

通过嵌入式满足接口

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
package main

import (
"fmt"
"strings"
)

type talker interface {
talk() string
}
type martian struct{}
type laser int
type starship struct {
laser
}

func shout(t talker) {
louder := strings.ToUpper(t.talk()) //strings.ToUpper,将字符串修改为大写格式
fmt.Println(louder)
}
func (m martian) talk() string {
return "martian"
}
func (l laser) talk() string {
return "laser"
}
func main() {
var la laser
s := starship{la}
fmt.Println(s.talk())
shout(s)

}

探索接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package main

import (
"fmt"
"time"
)

// 为给定日期返回一个虚假日期
func stardate(t time.Time) float64 { //time.Time()声明时,若未初始化则表示UTC时间,公元1年,1月,1日,0时,0分,0秒
doy := float64(t.YearDay())
h := float64(t.Hour()) / 24.0
return 1000 + doy + h
}

func main() {
day := time.Date(2023, 3, 11, 10, 11, 0, 0, time.UTC)
fmt.Printf("%f好奇号火星探测器着陆", stardate(day))
}

上面这个虚假的计算星历的函数只能使用地球日期作为输入,我们接下来利用接口实现更复杂的输入

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
package main

import (
"fmt"
"time"
)

type stardater interface {
YearDay() int
Hour() int
}
type sol int

func (s sol) YearDay() int {
return int(s % 668) //一火星年有668火星日
}
func (s sol) Hour() int {
return 0
}
func stardate(t stardater) float64 {//修改函数接受类型为stardater接口
doy := float64(t.YearDay())
h := float64(t.Hour()) / 24.0
return 1000 + doy + h
}

func main() {
day := time.Date(2023, 3, 11, 10, 11, 0, 0, time.UTC)
fmt.Printf("%f好奇号火星探测器着陆\n", stardate(day))

s := sol(1422)
fmt.Println(stardate(s))
}

满足接口

go语言标准库导出了很多只有单个方法的接口,开发者可以在自己的代码中实现他们

例如fmt包的Stringer接口,一种类型只要提供了String方法,他的值就可以被Println等函数打印

image-20230311104240576

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package main

import "fmt"

type location struct {
lat, long float64
}

func (l location) String() string {
return "0"
}

func main() {
curiosity := location{-4.3243, 137.1111}
fmt.Println(curiosity)
}

这个时候聪明的小伙伴会有疑惑了,为什么我们平时打印文本或者其他各种类型的变量之前也妹实现接口啊,为啥也能打印呢

image-20230311105525460

image-20230311105551928

空接口没有任何方法,因此任何类型都无须实现空接口。

从实现的角度看,任何值都满足这个接口的需求。因此空接口类型可以保存任何值,也可以从空接口中取出原值。

so,任何值都可以被Println这个函数接受,Printf,Print同理