结构或对象

通过type 关键字定义一个自定义的类型

type identifier struct {
field1 type1
field2 type2

}

比如user 类型

1
2
3
4
5
6
type User struct{
Id string
Name string
Age int
}

自定义类型创建的时候使用 new 关键字创建

1
2
3
4
5
6
7
func TestUser(t *testing.T) {
u := new(User)
u.Age = 10
u.Name = "小明"
u.Id = "1"
t.Log(u)
}

也支持多种创建方式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
func TestCreate(t *testing.T) {
//无参数创建
u1 := new(User)
//根据参数顺序创建
u2 := User{"1","小明",12}
//执行参数创建
u3 := User{Name:"小红"}
//引用的方式创建
u4 := &User{"1","小明",12}
t.Log(u1)
t.Log(u2)
t.Log(u3)
}

注意:通过new 方式获取的对象是一个指针类型

&User 的方式创建返回的是一个引用,底层仍然会调用new

使用new 创建的对象 是创建了一个指针对象,指针对象指向实际的对象,而不使用new 返回的是结构体的对象

结构体的指针字段

通过在字段类型上加 * 号表示是一个指针引用字段。当然也可以不加* 表示字段的类型直接是另一个结构体而不是引用.

如果是引用类型那么 指向的是同一个内存地址,只要修改都会修改,如果不是指针类型 那么相当于是一个拷贝,并不会随着修改

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
type Group struct {
Name string
}

type User struct{
Id string
Name string
Age int
Group *Group
}


func TestCreate(t *testing.T) {
u1 := new(User)
g := &Group{"第一部门"}
u1.Group = g
t.Log(u1)
t.Log(u1.Group)
}


对象的行为方法定义

函数名前声明函数的类型来表示函数属于那个方法

  1. 非指针类型
1
2
3
4

func (s Stuct) method(){

}
  1. 使用指针类型定义
1
2
3
func (s *Stuct) method(){

}

一般为了避免内存拷贝使用第二种方式来定义.如果第一种方式会将 实例执行一次复制

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

type Animal struct{
Name string
}


func (animal *Animal) eat() {
fmt.Print("eat")
}

func TestMethod(t *testing.T) {
ani := new(Animal)
ani.eat()
}

接口

接口定义了一组行为规范。
需要注意的是在go中,类型不需要显式声明它实现了某个接口:接口被隐式地实现。多个类型可以实现同一个接口。

go语言的接口的最大特点是非侵入性,可以不依赖于接口定义。

接口示例

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

type EatAble interface {
eat()
}

type Dog struct {

}

func (d *Dog) eat() {
fmt.Println("dog eat")
}

func TestInterface(t *testing.T) {
var e EatAble
e = new(Dog)
e.eat()
}

实现类和接口的关系并没有强制绑定。

空接口

空接口不包含任何方法,任何其他的类型认为实现了空接口,空接口可以表示任何类型。

示例,根据空接口判断类型

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
//类型判断
func typeJudge(any interface{}) string {
switch any.(type) {
case bool:
return "bool 类型"
case int:
return "int 类型"
case float32:
return "float32"
default:
return "不识别的类型"
}
}

func TestTypeJudge(t *testing.T) {
v := 10
ret := typeJudge(v)
t.Log(ret)
v2 := "束带结发"
ret = typeJudge(v2)
t.Log(ret)

}


go接口建议

  • 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 Pet struct {
}

func (p *Pet) eat() {
fmt.Println("吃饭了....")
}

type Pig struct {
pet *Pet
}

func (p *Pig) eat() {
p.pet.eat()
fmt.Println("猪要吃饭了===")
}

func TestPigEat(t *testing.T) {
p := new(Pig)
p.eat()
}


多态

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
27
28
29
30
31
32
33
34
35
36
//接口
type Runable interface {

run()
}
//实现1
type Person struct {

}
//实现2
type Rabbit struct {

}
//实现1 的方法
func (p *Person)run(){
fmt.Println("预备 。。跑")
}
//实现2的方法
func (r *Rabbit)run(){
fmt.Println("兔子。。跳")
}

//执行方法
func doRun(r Runable) {
r.run()
}
//测试方法,多态调用
func TestPolymorphic(t *testing.T) {
var r Runable
r = new(Person)
doRun(r)
r = new(Rabbit)
doRun(r)
}