封装、继承、多态
一、封装
封装可以使字段不直接暴露,通过自己封装的函数、结构体绑定的方法来初始化、获取或者修改等操作。(类似java中的构造函数、get、set的作用)
// 结构体都是小写,其他包无法访问到,所以需要封装一些方法去访问到该结构体
type person struct {
number string
Password string
}
// 初始化person结构体对象,并且返回该结构体执政
func NewPerson() *person {
return &person{}
}
// 修改person中number字段的方法
func (p *person) SetNumber(number string) error {
if len(number) > 6 && len(number) < 10 {
p.number = number
} else {
return errors.New("失败")
}
return nil
}
二、继承
1、匿名继承
- 结构体可以使用匿名继承的所有字段,方法(其中包含私有 or 公有的方法)
匿名结构体
按照究竟访问原则,即子类结构体中如果有与父类相同字段number
则p.number
优先获取子类的number字段值;如果子类结构体中没有该字段,则结构体依次访问子类结构体的字段,直到找到为为止!!!- 多继承关系,有多个父类结构体有相同字段(本身没有改同名字段),则必须使用
结构体.父类结构体.字段
获取该字段,否则编译异常。
type person struct {
number string
Password string
}
type HPerson struct {
person
Ext string
}
// person 绑定的方法
func (p *person) SetNumber(number string) error {
if len(number) > 6 && len(number) < 10 {
p.number = number
} else {
return errors.New("失败")
}
return nil
}
func main() {
hp := HPerson{person: {"number", "password"}, Ext: "ext"}
// 匿名继承可以直接调用父类的字段属性和方法
// 方法1
hp.number = "123456"
hp.SetNumber("1234567")
// 方法2
hp.person.number = "123456"
hp.person.SetNumber("1234567")
}
2、具名继承
具名继承,故名思议,就是继承的结构体指定了名称(有些叫组合关系)
具名继承调用该结构体的方法、字段时,必须携带该结构体名,否则无法编译。
我们来对比下
-
匿名继承
-
具名继承
三、多态
golang中多态的特性主要是通过结构实现的
3-1 接口(引用类型)
-
什么是接口?
interface可以定义一组方法,但都不需要实现。并且不包含任何变量。自定义结构体绑定方法需要使用时,再去实现接口中的方法。 -
如何使用?
golang中的接口不需要任何显式实现。自定义变量需要实现接口所有的方法,呢么就可以实现接口的使用。 -
注意事项!
- 一个类可以实现多个接口
- 只要是自定义数据类型就可以实现接口
- 一个接口A可以继承多个接口,实现A接口必须将B、C接口实现
- 接口是引用类型,没有初始化,输出的是nil
-
接口快速搭建
type Usb interface { Start() Stop() } // Phone结构体 type Phone struct { Number int } // Phone绑定的方法,实现Usb接口的方法 func (p *Phone) Start() { fmt.Println("Phone开始") } func (p *Phone) Stop() { fmt.Println("Phone停止") } // Camera结构体 type Camera struct { } // Camera绑定的方法,实现Usb接口的方法 func (p *Camera) Start() { fmt.Println("Camera开始") } func (p *Camera) Stop() { fmt.Println("Camera停止") } // 电脑结构体 type Computer struct { } // 接收一个usb接口类型的参数变量 // 实现了usb接口 func (c *Computer) Work(usb Usb) { usb.Start() fmt.Println("开始运行") usb.Stop() }
-
接口变量
指向接口变量的自定义数据类型,该类型下必须实现接口定义的方法// Phone指针类型的结构体绑定的方法 // 所以也得是Phone指针类型的变量才有这些方法!!!! func (p *Phone) Start() { fmt.Println("Phone开始") } func (p *Phone) Stop() { fmt.Println("Phone停止") } func main(){ phone := &other.Phone{} // 接口不可以实例化,但可以指向实现接口方法的自定义类型的变量 var myin other.Usb = phone }
-
范型的应用
定义一个空接口,没有实现任何方法,即表示所有类都实现了该接口,可以接收任何类型的值,也就是常说的范型
// 接收范型类型的参数 func testFunc(i interface{}) { fmt.Printf("%T\n", i) } func main(){ testFunc(1) // 输出 =》int testFunc(1.222) // 输出 =》float64 testFunc("1") // 输出 =》string }
-
源码案例:
sort.Sort(data interface{})
type Phones []int func (ps Phones) Len() int { return len(ps) } func (ps Phones) Less(i, j int) bool { return ps[i] < ps[j] } func (ps Phones) Swap(i, j int) { ps[i], ps[j] = ps[j], ps[i] } func Sort() { // 自定义类型Phones下实现了Sort接收接口参数的所有方法 // 所以直接将ps变量传递给接口,接口会指向ps绑定方法的内存地址,并且调用 ps := Phones{5,1,2,41,3} // Sort 接收一个接口作为参数(该变量需要实现Len() Less() Swap() 方法) sort.Sort(ps) fmt.Println(ps) }
3-2 类型断言
类型断言是可以通过接口来转化成对应的自定义结构,相对于空接口来说,只可以转化成实现接口方法的自定义数据结构。
-
实现简单的接口断言
断言转化后的数据类型,就相当于数据类型本身,可以调用该结构体自身的所有方法。
转化公式:接收转化后的变量, 是否转化成功 := 接口变量.(类型)
type Phone struct { Number int } func (c *Computer) Work(usb Usb) { // *Phone类型下有没有绑定这个方法 usb.Start() fmt.Println("开始运行") usb.Stop() // 将usb转化为Phone类型 // 接收转化后的变量, 是否转化成功 := 接口变量.(类型) phone, ok := usb.(*Phone) if ok { // 转化成功 fmt.Println(phone.Number) } else { // 转化失败 fmt.Println("不是Phone类型") } }
如果是未实现接口的类型,则会提示如下
-
switch和接口断言
接口.(类型)
只适用于switch// 判断传入的类型 func inters(TS ...interface{}) { for _, T := range TS { switch T.(type) { // 接口.(类型) 只适用于switch case float64: fmt.Println("float") case string: fmt.Println("string") case other.Phone: fmt.Println("Phone类型") default: fmt.Println("失败") } } }