结构或对象
通过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 2 3 4
| func (s Stuct) method(){ }
|
- 使用指针类型定义
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) }
|