Golang - 面对"对象"
1. 简介
- go语言对于面向对象的设计非常简洁而优雅
- 没有封装、继承、多态这些概念,但同样通过别的方式实现这些特性
- 封装:通过方法实现
- 继承:通过匿名字段实现
- 多态:通过接口实现
2. 匿名字段
go支持只提供类型而不写字段名的方式,也就是匿名字段,也称为嵌入字段
//package 声明开头表示代码所属包
package main
import "fmt"
//定义人的结构体
type Person struct {
name string
sex string
age int
}
//学生
type Student struct {
//匿名字段
//默认Student包含了Person所有字段
Person
id int
addr string
}
func main() {
s2 := Student{Person:Person{"约汉","female",10},id:2}
fmt.Println(s2)
}
//{{约汉 female 10} 2 }
同名字段的情况
//package 声明开头表示代码所属包
package main
import "fmt"
//定义人的结构体
type Person struct {
name string
sex string
age int
}
//学生
type Student struct {
//匿名字段
//默认Student包含了Person所有字段
Person
id int
addr string
//同名字段
name string
}
func main() {
var s Student
//就近赋值
s.name = "约汉"
fmt.Println(s)
//若给外面赋值
s.Person.name = "接客"
fmt.Println(s)
}
//{{ 0} 0 约汉}
//{{接客 0} 0 约汉}
所有的内置类型和自定义类型都是可以作为匿名字段去使用
package main
import "fmt"
//定义人的结构体
type Person struct {
name string
sex string
age int
}
//自定义类型
type mystr string
//学生
type Student struct {
//匿名字段
//默认Student包含了Person所有字段
Person
//内置
int
mystr
}
func main() {
//初始化
s1 := Student{Person{"约汉","male",18},1,"bj"}
fmt.Println(s1)
fmt.Println(s1.name)
}
指针类型匿名字段
//package 声明开头表示代码所属包
package main
import "fmt"
//定义人的结构体
type Person struct {
name string
sex string
age int
}
//学生
type Student struct {
//匿名字段
//默认Student包含了Person所有字段
*Person
//内置
id int
addr string
}
func main() {
s1 := Student{&Person{"约汉","male",18},1,"bj"}
fmt.Println(s1)
fmt.Println(s1.name)
}
//{0xc0420661e0 1 bj}
//约汉
3. 方法
4. 包和封装
5. 接口
- 在面向对象编程中,一个对象其实也就是一个简单的值或者一个变量,在这个对象中会包含一些函数
- 这种带有接收者的函数,我们称为方法,本质上,一个方法则是一个和特殊类型关联的函数
- 方法的语法如下
- func (接收参数名 接收类型) 方法名(参数列表)(返回值)
- 可以给任意自定义类型(包括内置类型,但不包括指针类型)添加相应的方法
- 接收类型可以是指针或非指针类型
- 为类型添加方法(为基础类型添加方法和为结构体类型添加方法)
-
基础类型
//package 声明开头表示代码所属包 package main import "fmt" //任务:定义方法实现2个数相加 type MyInt int //传统定义方式,面向过程 func Add(a, b MyInt) MyInt { return a + b } //面向对象 func (a MyInt) Add(b MyInt) MyInt { return a + b } func main() { var a MyInt = 1 var b MyInt = 1 fmt.Println("Add(a,b)=", Add(a, b)) //调用面向对象的方法 fmt.Println("a.Add(b)=",a.Add(b)) } //Add(a,b)= 2 //a.Add(b)= 2
-
结构体类型
//package 声明开头表示代码所属包 package main import "fmt" type Person struct { name string sex string age int } //为Person添加方法 func (p Person) PrintInfo() { fmt.Println(p.name, p.sex, p.age) } func main() { p := Person{"接客", "male", 18} p.PrintInfo() }
-
值语义和引用语义
//package 声明开头表示代码所属包 package main import "fmt" type Person struct { name string sex string age int } //设置指针作为接收者的方法,引用语义 func (p *Person) SetInfoPointer() { (*p).name = "接客" p.sex = "female" p.age = 22 } //值作为接收者,值语义 func (p Person) SetInfoValue() { p.name = "约汉" p.sex = "male" p.age = 20 } func main() { //指针作为接收者的效果 p1 := Person{"撸死", "male", 19} fmt.Println("函数调用前=", p1) (&p1).SetInfoPointer() fmt.Println("函数调用后=", p1) fmt.Println("================寂寞的分割线=================") //值作为接收者的效果 p2 := Person{"约汉", "male", 18} fmt.Println("函数调用前=", p2) p2.SetInfoValue() fmt.Println("函数调用后=", p2) } //函数调用前= {撸死 male 19} //函数调用后= {接客 female 22} //================寂寞的分割线================= //函数调用前= {约汉 male 18} //函数调用后= {约汉 male 18}
-
方法的继承
package main import "fmt" type Person struct { name string sex string age int } //为Person定义方法 func (p *Person) PrintInfo() { fmt.Printf("%s,%s,%d ",p.name,p.sex,p.age) } type Student struct { Person id int addr string } func main() { p :=Person{"接客","male",18} p.PrintInfo() //学生也去调,方法继承 s := Student{Person{"接客","male",18},2,"bj"} s.PrintInfo() }
-
方法的重写
package main import "fmt" type Person struct { name string sex string age int } //为Person定义方法 func (p *Person) PrintInfo() { fmt.Printf("%s,%s,%d ", p.name, p.sex, p.age) } type Student struct { Person id int addr string } //Student定义方法,实际上就相当于方法重写 func (s *Student) PrintInfo() { fmt.Printf("Student:%s,%s,%d ", s.name, s.sex, s.age) } func main() { p := Person{"接客", "male", 18} p.PrintInfo() //学生也去调,方法继承 s := Student{Person{"接客", "male", 18}, 2, "bj"} s.PrintInfo() //显式调用 s.Person.PrintInfo() }
-
方法值和方法表达式
package main import "fmt" type Person struct { name string sex string age int } func (p *Person) PrintInfoPointer() { //%p是地址,%v是值 fmt.Printf("%p,%v ", p, p) } func main() { p := Person{"接客", "male", 18} //传统的调用方法的方式 p.PrintInfoPointer() //使用go方法值特性调用 pFunc1 := p.PrintInfoPointer pFunc1() //使用go方法表达式调用 pFunc2 := (*Person).PrintInfoPointer pFunc2(&p) }
-
练习:创建属性的getter和setter方法并进行调用
package main
import "fmt"
type Dog struct {
name string
//1公 0母
sex int
}
//封装dog的方法
//setter
func (d *Dog) SetName(name string) {
d.name = name
}
//getter
func (d *Dog) GetName() string {
return d.name
}
//咬人
func (d *Dog) bite() {
fmt.Printf("让本汪%s 来给你上课...", d.name)
}
func main() {
d := Dog{"二哈", 1}
d.bite()
}
4. 包和封装
- 方法首字母大写:public
- 方法首字母小写:private
- 为结构体定义的方法必须放在同一个包内,可以是不同的文件
- 上面代码复制到test包中,在test02包中进行调用,需要调用的方法名首字母大写
5. 接口
-
go语言中,接口(interface)是一个自定义类型,描述了一系列方法的集合
-
接口不能被实例化
-
接口定义语法如下
-
type 接口名 interface{}
-
PS:接口命名习惯以er结尾
-
接口定义与实现
package main import "fmt" //定义人的接口 type Humaner interface { //说话 Say() } //学生结构体 type Student struct { name string score int } //Student实现Say()方法 func (s *Student) Say() { fmt.Printf("Student[%s,%d] 瞌睡不断 ", s.name, s.score) } type Teacher struct { name string group string } //老师实现接口 func (t *Teacher) Say() { fmt.Printf("Teacher[%s,%s] 毁人不倦 ", t.name, t.group) } //自定义类型 type MyStr string //自定义类型实现方法 func (str MyStr) Say() { fmt.Printf("MyStr[%s] 同志醒醒,还有个bug ", str) } func WhoSay(i Humaner) { i.Say() } func main() { s := &Student{"约汉", 88} t := &Teacher{"撸死", "Go语言"} var tmp MyStr = "接客" s.Say() t.Say() tmp.Say() //go的多态,调用同一个接口,不同表现 WhoSay(s) WhoSay(t) WhoSay(tmp) //make()创建 x := make([]Humaner, 3) x[0], x[1], x[2] = s, t, tmp for _, value := range x { value.Say() } }
-
接口继承
package main import "fmt" //定义人的接口 type Humaner interface { //说话 Say() } type Personer interface { //等价于写了Say() Humaner Sing(lyrics string) } //学生结构体 type Student struct { name string score int } //Student实现Say()方法 func (s *Student) Say() { fmt.Printf("Student[%s,%d] 瞌睡不断 ", s.name, s.score) } func (s *Student) Sing(lyrics string) { fmt.Printf("Student sing[%s]!! ", lyrics) } func main() { s := &Student{"约汉", 88} var p Personer p = s p.Say() p.Sing("互撸娃") }
-
空接口:空interface{}不包含任何方法,空接客可以存储任意类型的值
-
类型查询
-
comma-ok断言
package main import "fmt" //空接口 type Element interface{} type Person struct { name string age int } func main() { //切片 list := make([]Element, 3) //int list[0] = 1 list[1] = "Hello" list[2] = Person{"luhan", 18} //遍历 for index, element := range list { //类型断言:value ,ok = element.(T) //value 是变量的值,ok是返回的布尔值,element是接口变量,T是断言类型 if value, ok := element.(int); ok { fmt.Printf("list[%d]是int类型,值是%d ", index, value) } else if value, ok := element.(int); ok { fmt.Printf("list[%d]是int类型,值是%d ", index, value) } else if value, ok := element.(Person); ok { fmt.Printf("list[%d]是Person类型,值是[%s,%d] ", index, value.name, value.age) } else { fmt.Printf("list[%d]是其他类型 ", index) } } }
-
switch测试
package main import "fmt" //空接口 type Element interface{} type Person struct { name string age int } func main() { //切片 list := make([]Element, 3) //int list[0] = 1 list[1] = "Hello" list[2] = Person{"luhan", 18} //遍历 for index, element := range list { switch value := element.(type) { case int: fmt.Printf("list[%d]是int类型,值是%d ", index, value) case string: fmt.Printf("list[%d]是string类型,值是%s ", index, value) default: fmt.Printf("list[%d]是其他类型 ", index) } } }
-
-