结构体

结构体内嵌

使用结构体内嵌是一种面向对象编程思想中的继承关系

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
type Book struct {
title string
author string
num int
id int
}

type BookBorrow struct {
Book // 继承了Book的基本属性和方法
borrowTime string
}

type BookNotBorrow struct {
Book // 继承了Book的基本属性和方法
readTime string
}

结构体方法

方法和函数比较像,区别在于函数属于包,通过包调用函数;方法属于结构体,通过结构体变量调用

1
2
3
func (变量名 结构体类型) 方法名(参数列表) (返回值列表) {
// 方法体
}

接口

GO语言接口是方法的集合,使用接口是实现模块化的重要方式

接口的创建与实现

接口是用来定义行为类型的,这些被定义的行为不由接口直接实现,而是通过方法由用户定义的类型实现,一个实现了这些方法的具体类型被称为接口的实例

接口中声明完方法后,结构体重写接口中所有的方法,即认为结构体实现了接口

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
type Transport interface {
LocalResolveTCPAddr(address string) (*net.TCPAddr, error)
LocalDialTCP(laddr, raddr *net.TCPAddr) (*net.TCPConn, error)
LocalListenTCP(laddr *net.TCPAddr) (*net.TCPListener, error)
}

type defaultTransport struct {
Timeout time.Duration
}

func (t *defaultTransport) LocalResolveTCPAddr(address string) (*net.TCPAddr, error) {
return net.ResolveTCPAddr("tcp", address)
}

func (t *defaultTransport) LocalDialTCP(laddr, raddr *net.TCPAddr) (*net.TCPConn, error) {
dialer := &net.Dialer{Timeout: t.Timeout, LocalAddr: laddr}
conn, err := dialer.Dial("tcp", raddr.String())
if err != nil {
return nil, err
}
return conn.(*net.TCPConn), nil
}

func (t *defaultTransport) LocalListenTCP(laddr *net.TCPAddr) (*net.TCPListener, error) {
return net.ListenTCP("tcp", laddr)
}

上面代码创建了一个 Transport 的接口,结构体 defaultTransport 实现了 Transport 接口中的所有方法,即认为 defaultTransport 实现了 Transport 接口, defaultTransport 就是 Transport 接口的实例

多态

同一件事情由于条件不同产生的结果不同即为多态,多态在代码层面最常见的一种方式是接口作为方法参数

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 Live interface {
run()
}
type People struct{}
type Animate struct{}

//方法值使用的指针类型
func (p *People) run() {
fmt.Println("人在跑")
}
func (a *Animate) run() {
fmt.Println("动物在跑")
}

//接口作为方法参数
func sport(live Live) {
live.run()
}

func main() {
peo := &People{}
sport(peo) //输出:人在跑
ani := &Animate{}
sport(ani) //输出:动物在跑
}

结构体实现了接口的全部方法,就认为结构体属于接口类型,这时可以把结构体变量赋值给接口变量

上面需要注意的一点是,方法集如果使用的是指针类型(如 p *People )那么必须传入指针类型的值;如果方法集使用的是值类型(如 p People )那么可以传入指针类型的值也可以传入值类型的值