把一类事物共有的属性(字段)和行为(方法)提取出来,形成一个物理模型(结构体)。这种研究问题的方法称为抽象。
封装就是把抽象出来的字段和对字段的操作封装在一起,数据被保护在内部,程序的其他包只有通过被授权的操作(方法),才能对字段进行操作。
2.1 封装的好处
2.2 封装的实现步骤
3.1 出现原因:
①小学生pupil,中学生,大学生graduate等结构体的字段和方法几乎相同,代码复用性不强
②代码冗余,而且代码不利于维护,也不利于功能扩展
③解决办法:通过继承方式来解决-》通过嵌套匿名结构体来实现
3.2 深入讨论--细节
对上面的代码总结:
1、当然直接通过b访问字段或方法时,其执行流程如下:
2、编译器会先看b对应的类型有没有Name,如果有,则直接调用B类型的Name字段
3、如果没有,就去看B中嵌入的匿名结构体A有没有Name字段,如果有,就调用,如果没有继续找...如果都找不到就报错
3.3 多重继承
如果一个结构体嵌套了多个匿名结构体,那么该结构体可以直接访问嵌套匿名结构体的字段和方法,从而实现多重继承。
Golang中多态特性主要是通过接口来体现的。
接口类型可以定义一组方法,不需要实现,而且接口中不能包含任何变量(Java接口里面可以包含变量)。到某个自定义类型要使用的时候,在根据具体情况把这些方法进行实现。
4.1 基本语法
总结:
① 接口里所有的方法都没有方法体。接口体现了程序设计的多态和高内聚低耦合的思想。
② Golang的接口,不需要显式的实现,且没有implement关键字。只要一个变量含有接口类型中的所有方法,那么这个变量就实现了这个接口。
4.2 快速入门案例...
4.3 注意事项和细节
(8)一个接口如A可以继承多个接口如B和C,那么要实现A接口,也必须将B和C接口的方法全部实现
(9)接口类型默认是一个指针(引用类型),如果没有对接口初始化就使用,那么会输出nil
(10)空接口interface{}没有任何方法,所有所有类型都可以实现空接口。即我们可以把任何一个变量赋值给空接口
4.5 实践--对自定义数据类型排序
package main
import (
"fmt"
)
// 结构体
type Monkey struct {
Name string
}
func (this *Monkey) Climbing() {
fmt.Println(this.Name, "生来会爬树")
}
// 接口
type BirdAble interface {
Flying()
}
type FishAble interface {
Swimming()
}
// 新的结构体
type LittleMonkey struct {
Monkey // 继承了Monkey接口
}
// LittleMonkey实现了上面两个接口
func (this *LittleMonkey) Flying() {
fmt.Println(this.Name, "通过学习,学会爬树...")
}
func (this *LittleMonkey) Swimming() {
fmt.Println(this.Name, "通过学习,学会游泳...")
}
func main() {
// 创建一个LittleMonkey实例/变量
littleMonkey := LittleMonkey{
Monkey{
Name : "悟空",
},
}
littleMonkey.Climbing()
littleMonkey.Flying()
littleMonkey.Swimming()
}
对上面代码的总结:
1、当A结构体继承了B结构体,那么A就自动继承了B的所有字段和方法,并且可以直接使用
2、当A结构体需要扩展功能,同时又不希望去破坏继承关系,可以通过去实现某个接口即可。因为我们可以认为:实现接口是对继承机制的补充。
(1)接口和继承解决的问题不同:
继承的价值在于:解决代码的复用性和可维护性。
接口的价值在于:设计,设计好各种规范(方法),让其他自定义类型去实现这些方法。
(2)接口比继承更加灵活
继承是满足is-a的关系,而接口只需满足like-a的关系。
(3)接口在一定程序上实现代码解耦
变量/实例具有多种形态。在Go中,多态特征是通过接口实现的。
6.1 基本介绍
类型断言,由于接口是一般类型不知道具体类型,如果要转成具体类型,就需要使用类型断言。如果指向要转成的类型,即类型匹配,ok;否则报错panic。示例如下:
6.2 进行断言时,带上检测机制,成功就ok,失败也不要报panic