这是我参与「第三届青训营 -后端场」笔记创作活动的第1篇笔记
继承
继承实现方法
在Go中,继承采用匿名结构体实现。
type Student struct{
Name string
Age int
}
type Graduate struct{
Student // 继承Student
id int
}
type PostGraduates struct{
Student // 继承Student
Tutor string
}
继承的细节
- 结构体和嵌入的匿名结构体中,有相同的字段和方法时,编译器采用就近原则。要访问匿名结构体的字段时,加上匿名结构体来区分
type A struct{
Name string
}
type B struct{
A // 继承A
Name string
}
func main() {
var b B
b.A.Name = "marry"
fmt.Println(b.Name) // 空串
fmt.Println(b.A.Name) // marry
b.Name = "smith"
fmt.Println(b.Name) // smith
}
- 嵌入匿名结构体可以使用指针
type Person struct{
Name string
Age int
}
type Info struct{
Id int
Score int
}
type Student struct{
*Person
*Info // 多重继承,但并不推荐(多重继承使继承关系混乱)
}
func main() {
stu := Student{
&Person{"john", 18},
&Info{001, 90},
}
}
- 嵌入多个匿名结构体,可以实现多重继承。因为多重继承使得继承关系混乱,所以并不推荐多重继承
接口
若多个类型都有一个或者多个共同点,那么就可以将这些共同的抽象出来聚合在一起,形成接口。接口与现实中的接口类似,如USB接口,USB接口可以插入手机、鼠标、键盘等设备,只要符合USB标准即可。
接口实现了高内聚,低耦合的思想
接口实现方法
Go中的接口,不需要显式的实现。只要一个变量,含有接口类型中的所有方法,那么这个变量就实现了这个接口。一般定义方法如下:
type InterfaceName interface{
// method1(参数利列表) 返回值列表
// method2(参数利列表) 返回值列表
// ...
}
下面抽象的定义一个USB接口
type Usb interface{ // 定义一个USB接口
Work()
Stop()
}
type Phone struct{ // 手机
// ...
}
func (p Phone) Work() {
// ...
}
func (p Phone) Stop() {
// ...
}
type Mouse struct{ // 鼠标
// ...
}
func (m Mouse) Work() {
// ...
}
func (m Mouse) Stop() {
// ...
}
type Computer struct {
// ...
}
func (c Computer) Working(usb Usb) {
usb.Start() // USB接口开始工作
if phone, ok := usb.(Phone); ok { // 类型断言
// 若是手机,执行一些特殊操作
// ...
}
usb.Stop() // USB接口停止工作
}
func main() {
computer := Computer{}
phone := Phone{}
mouse := Mouse{}
computer.Working(phone) // 接口“插入手机”
computer.Working(mouse) // 接口“插入鼠标”
}
接口的细节
- 接口的实现是指,一个类型实现了被定义接口内的所有方法(隐式实现)。一个自定类型可以实现多个接口
- 接口不能创建变量,但可以指向一个实现了该接口的自定义类型(不仅仅是结构体)的实例
type A interface{
test()
}
type Student struct{
Name string
}
// Student 实现了A接口
func (stu Student) test() {
fmt.Println("Stu test()")
}
func main() {
var stu Student // Stu 实现了A接口
var a A = stu
a.test() // "Stu test()"
}
- 接口之间可以有继承关系,要实现所有被继承的接口
type B interface{
test1()
}
type C interface{
test2()
}
type A interface{
B // 继承B接口
C // 继承C接口
test3()
}
type Student struct {
Name string
}
// 用Student实现A接口:要实现所有被继承的接口
func (stu Student) test1() {
//...
}
func (stu Student) test2() {
//...
}
func (stu Student) test3() {
//...
}
- interface是引用类型(指针)
- 空接口interface{}没有实现任何方法,所以所有类型都实现了空接口,可以把任何变量都赋值给空接口
type T interface{
// 空接口
}
func main() {
num1 := 6.17
num2 := 6
var t1 T = num1
fmt.Println(t1) // 6.17
var t2 interface{} = num2 // interface{}同样表示空接口
fmt.Println(t2) // 6
}
实践:实现Interface接口,对结构体切片进行排序
一个满足sort.Interface接口的(集合)类型可以被本包的函数进行排序。
type Interface interface {
// Len方法返回集合中的元素个数
Len() int
// Less方法报告索引i的元素是否比索引j的元素小
Less(i, j int) bool
// Swap方法交换索引i和j的两个元素
Swap(i, j int)
}
以Hero结构体实现Interface接口,对Hero的年龄进行排序:
package main
import (
"fmt"
"math/rand"
"sort"
)
type Hero struct {
Name string
Age int
}
// HeroSlice 是Hero结构体的切片类型
type HeroSlice []Hero
func (hs HeroSlice) Len() int {
return len(hs)
}
// Less 方法就是决定用什么标准排序
// 对Hero的年龄从小到大排序
func (hs HeroSlice) Less(i, j int) bool {
return hs[i].Age < hs[j].Age
}
func (hs HeroSlice) Swap(i, j int) {
hs[i], hs[j] = hs[j], hs[i]
}
func main() {
var heroes HeroSlice
for i := 0; i < 5; i++ {
hero := Hero{
Name: fmt.Sprintf("英雄-%d", rand.Intn(100)),
Age: rand.Intn(150),
}
heroes = append(heroes, hero)
}
// 排序前的顺序
fmt.Println("========排序前========")
for _, v := range heroes {
fmt.Println(v)
}
// 利用sort.Sort排序
sort.Sort(heroes)
fmt.Println("========排序后========")
for _, v := range heroes {
fmt.Println(v)
}
}
/*
========排序前========
{英雄-81 87}
{英雄-47 59}
{英雄-81 18}
{英雄-25 140}
{英雄-56 0}
========排序后========
{英雄-56 0}
{英雄-81 18}
{英雄-47 59}
{英雄-81 87}
{英雄-25 140}
*/
接口和继承的关系
-
接口的实现可以看作对继承的补充
-
接口和继承的解决问题不同:
- 继承:解决代码复用性和可维护性
- 接口:设计,设计和各种规范(方法),让其他自定义类型去实现这些方法
-
接口比继承更灵活:
- 继承是is-a的关系
- 接口只需满足like-a的关系
-
接口在一定程度上实现了代码的解耦
多态
在GO中,多态是通过接口实现的。可以安装统一的接口调用不同的实现,这是接口变量就程序不同的形态。
接口体现多态
2种形式如下:
-
多态参数:以接口作为函数的参数
-
多态数组:接口数组可以存放任何实现了该接口的变量
- 空接口数组任何类型都可以存放
类型断言
使用方法
由于接口是一般类型,不知道具体类型,若要从接口转换成具体类型,就需要类型断言
var z float32 = 1.1
var x interface{} // 空接口
x = z // 空接口可以接收任意类型
y, ok := x.(float32) // 类型断言,y=1.1
if ok {
// 断言成功
} else {
// 断言失败
}
// 不论断言成功与否,代码继续执行
// ....
\