结构和方法

结构体工厂

Go 中不支持向面向对象语言的方法那样构造类的子方法,但是可以根据构造子工厂的方法来实现子方法。一般来说,工厂的名字用 New 开头。

1
2
3
4
5
6
7
8
9
10
11
type File struct {
fd int
name string
}

func NewFile(fd int, name string) *File {
if fd < 0 {
return nil
}
return &File{fd, name}
}

如果结构体的命名开头是小写的话,可以强制要求使用结构体工厂。

匿名字段和内嵌结构体

结构体可以包含一个或多个 匿名(或内嵌)字段,即这些字段没有显式的名字,只有字段的类型是必须的,此时类型就是字段的名字。匿名字段本身可以是一个结构体类型,即 结构体可以包含内嵌结构体

可以粗略地将这个和面向对象语言中的 继承 概念相比较,随后将会看到它被用来模拟类似继承的行为。Go 语言中的继承是通过内嵌或组合来实现的,所以可以说,在 Go 语言中,相比较于继承,组合更受青睐。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// 内嵌结构体示意
type A struct {
ax, ay int
x int
y int
}

type B struct {
A
bx, by float32
// 命名冲突保留外层的 类似重载
x int
y string
}

func main() {
b := B{A{1, 2, 3, 4}, 5.0, 6.0, 7, "8"}
fmt.Println(b.ax, b.ay, b.bx, b.by)
fmt.Println(b.A)
fmt.Println(b.x)
}

方法

方法类似于面向对象语言中的 成员函数,不过 Go 虽然不会有一个 class 的概念,但是可以用以下的方法达到类似的效果。

1
2
3
func (recv receiver_type) methodName(parameter_list) (return_value_list) { 
//...
}

其中 recv 就类似与面向对象语言中的 self 或者 this。

需要注意的点:

  • 类型和作用在它上面定义的方法必须在同一个包里定义,这就是为什么不能在 int、float 或类似这些的类型上定义方法。
1
2
3
4
5
6
7
8
package main

import "container/list"

// 这种做法是不被允许的
func (p *list.List) Iter() {
// ...
}
  • 但是有一个间接的方式:可以先定义该类型(比如:int 或 float)的别名类型,然后再为别名类型定义方法。或者像下面这样将它作为匿名类型嵌入在一个新的结构体中。当然方法只在这个别名类型上有效。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package main

import (
"fmt"
"time"
)

// 这样是可以的
type myTime struct {
time.Time //anonymous field
}

func (t myTime) first3Chars() string {
return t.Time.String()[0:3]
}

内嵌类型的方法和继承

  • 当一个匿名函数被内嵌在结构体中时,匿名类型的可见方法同样被内嵌,这在效果上等同于 方法继承

  • 可以覆写方法(像字段一样):和内嵌类型方法具有同样名字的外层类型的方法会覆写内嵌类型对应的方法。这样的覆写会优先调用外层的方法。

在类型中嵌入功能

  • 聚合:包含一个所需功能的具名字段
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

package main

import (
"fmt"
)

type Log struct {
msg string
}

type Customer struct {
Name string
log *Log
}

func main() {
c := new(Customer)
c.Name = "Barak Obama"
c.log = new(Log)
c.log.msg = "1 - Yes we can!"
// shorter
c = &Customer{"Barak Obama", &Log{"1 - Yes we can!"}}
// fmt.Println(c) &{Barak Obama 1 - Yes we can!}
c.Log().Add("2 - After me the world will be a better place!")
//fmt.Println(c.log)
fmt.Println(c.Log())

}

func (l *Log) Add(s string) {
l.msg += "\n" + s
}

func (l *Log) String() string {
return l.msg
}

func (c *Customer) Log() *Log {
return c.log
}

  • 内嵌:匿名地内嵌所需功能类型
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
package main

import (
"fmt"
)

type Log struct {
msg string
}

type Customer struct {
Name string
Log
}

func main() {
c := &Customer{"Barak Obama", Log{"1 - Yes we can!"}}
c.Add("2 - After me the world will be a better place!")
fmt.Println(c)
}

func (l *Log) Add(s string) {
l.msg += "\n" + s
}

func (l *Log) String() string {
return l.msg
}

func (c *Customer) String() string {
return c.Name + "\nLog:" + fmt.Sprintln(c.Log)
}

多重继承

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

package main

import (
"fmt"
)

type Camera struct{}

func (c *Camera) TakeAPicture() string {
return "Click"
}

type Phone struct{}

func (p *Phone) Call() string {
return "Ring Ring"
}

type CameraPhone struct {
Camera
Phone
}

func main() {
cp := new(CameraPhone)
fmt.Println("Our new CameraPhone exhibits multiple behaviors...")
fmt.Println("It exhibits behavior of a Camera: ", cp.TakeAPicture())
fmt.Println("It works like a Phone too: ", cp.Call())
}