面向对象
Golang接口的定义
1、Golang 中的接口
在Go语言中接口(interface)是一种类型,一种抽象的类型。
接口(interface)定义了一个对象的行为规范, 只定义规范不实现 ,由具体的 对象来实现规范的细 节 。
实现接口的条件
一个对象只要全部实现了接口中的方法 ,那么就实现了这个接口。
换句话说,接口就是一个需要实现的方法列表。
2、为什么要使用接口
上面的代码中定义了猫和狗,然后它们都会叫,你会发现main函数中明显有重复的代码
如果我们后续再加上猪、青蛙等动物的话,我们的代码还会一直重复下去
那我们能不能把它们当成“能叫的动物”来处理呢?
package main
import (
"fmt"
)
type Cat struct {
Name string
}
func (c Cat) Say() string { return c.Name + ":喵喵喵" }
type Dog struct {
Name string
}
func (d Dog) Say() string { return d.Name + ": 汪汪汪" }
func main() {
c := Cat{Name: "小白猫"} // 小白猫:喵喵喵
fmt.Println(c.Say())
d := Dog{"阿黄"}
fmt.Println(d.Say()) // 阿黄: 汪汪汪
}
/*
小白猫:喵喵喵
阿黄: 汪汪汪
*/
定义一个Usber接口
定义一个 Usber 接口让 Phone
和 Camera
结构体实现这个接口
package main
import "fmt"
/*
interface这个接口是golang中的一个数据类型(int、string)
和框架写的api接口没有任何关系,只是名字相同而已
*/
/*
1.函数编程:每个函数之间没有任何关系
2.结构体绑定方法:实现一类事物通用的方法,这些方法是有关系
定义一个人:eat方法、say方法 这些方法是有关系,都是定义了一个人能做的事情
*/
func main() {
// 第四步:初始化phone结构体
p := Phone{
Name: "Mate 30 pro",
}
// 第五步:通过接口来调用方法
var p1 Usber
p1 = p
Run(p1)
//p1.Start()
//p1.Stop()
//
//c := Computer{
// Name: "苹果",
//}
//var c1 Usber
//c1 =
// Run(p1)
//c.Start()
}
/*
第一句话:接口(interface)定义了一个对象的行为规范,
`只定义规范不实现`,由具体的`对象来实现规范的细节`
第二句话:一个对象只要全部实现了接口中的方法,那么就实现了这个接口。
*/
// 第一步:定义一个接口 , 接口的名字 xxxer结尾,只是一个规范
/*
Usber 是接口的名字
interface 定义接口的关键字
*/
type Usber interface {
// type Usber interface 就是定义了一个接口
// Start() / Stop() 定义必须要实现这两个方法(对象实现的细节)
Start()
Stop()
}
/* 应为 Phone没有实现Usber要求实现的规范:Stop、Start两个方法
Cannot use 'p' (type Phone) as the type Usber
Type does not implement 'Usber' as some methods are missing: Start()
*/
// 第二句话:一个对象只要全部实现了接口中的方法,那么就实现了这个接口。
// 第二步:定义一个结构体
type Phone struct {
Name string
}
// 第三步:Phone这个结构体实现了接口中定义的两个方法 Start、Stop
func (p Phone) Start() {
fmt.Println("start ...")
}
func (p Phone) Stop() {
fmt.Println("stop ...")
}
type Computer struct {
Name string
}
// 第三步:Phone这个结构体实现了接口中定义的两个方法 Start、Stop
func (c Computer) Start() {
fmt.Println("start ...")
}
func (c Computer) Stop() {
fmt.Println("stop ...")
}
func Run(u Usber) {
u.Stop()
u.Start()
}
go中类
Go语言中没有“类”的概念,也不支持“类”的继承等面向对象的概念。
Go语言中通过结构体的内嵌再配合接口比面向对象具有更高的扩展性和灵活性。
空接口
1、空接口说明
golang中空接口也可以直接当做类型来使用,可以表示任意类型 (泛型概念)
Golang 中的接口可以不定义任何方法,没有定义任何方法的接口就是空接口。
空接口表示没有任何约束,因此任何类型变量都可以实现空接口。
空接口在实际项目中用的是非常多的,用空接口可以表示任意数据类型。
2、空接口作为函数的参数
package main
import "fmt"
func main() {
show(1)
show("aa")
}
/*
interface{} 是一个空接口,空接口可以表示任意数据类型
*/
// 第一种:空接口作为函数的参数
func show(val interface{}) {
fmt.Printf("%T %v \n", val, val)
}
3、切片实现空接口
package main
import "fmt"
func main() {
// 第二种:切片实现空接口
//s1 := []int{1,2,3,"zhangsan"}
s1 := []interface{}{1, 2, "zhangsan"}
fmt.Println(s1)
}
4、map 的值实现空接口
package main
import "fmt"
func main() {
// 第三种:定义map的空接口
d := map[string]interface{}{
"name": "zhangsan",
"age": 24,
}
fmt.Println(d)
}
类型断言
一个接口的值(简称接口值)是由一个具体类型和具体类型的值两部分组成的。
这两部分分别称为接口的动态类型和动态值。
如果我们想要判断空接口中值的类型,那么这个时候就可以使用类型断言
其语法格式: x.(T)
x
: 表示类型为 interface{}
的变量
T
: 表示断言 x
可能是的类型
package main
import "fmt"
func main() {
// 第四种:断言使用
var x interface{}
x = 1
fmt.Printf("%T %v \n", x, x)
//x = x + 1
// 断言的使用:返回两个值,一个是变量本身值,第二个,ok是否是当前类型
v, ok := x.(int)
fmt.Println(v, ok)
v = v + 1
fmt.Println()
}
值接收者和指针接收者
1、值接收者
当方法作用于值类型接收者时,Go语言会在代码运行时将接收者的值复制一份。
在值类型接收者的方法中可以获取接收者的成员值,但修改操作只是针对副本,无法修改接收者变量本身。
package main
import "fmt"
type Usb interface {
Start()
Stop()
}
type Phone struct {
Name string
}
// 用了指针,只要任意一个地方修改,其他所有地方都会改是吧
func (p *Phone) Start() {
fmt.Println(p.Name, "开始工作")
}
func (p Phone) Stop() {
fmt.Println("phone 停止")
}
func main() {
//phone1 := Phone{ // 一:实例化值类型
// Name: "小米手机",
//}
//var p1 Usb = phone1 //phone1 实现了 Usb 接口 phone1 是 Phone 类型
//p1.Start()
// 如果接口中使用的是指针类型作为接收者,那么只能使用指针类型的结构体去调用
// 不能使用值类型的结构体调用
phone2 := &Phone{ // 二:实例化指针类型
Name: "苹果手机",
}
var p2 Usb = phone2 //phone2 实现了 Usb 接口 phone2 是 *Phone 类型
p2.Start() //苹果手机 开始工作
}
2、指针接收者
指针类型的接收者由一个结构体的指针组成
由于指针的特性,调用方法时修改接收者指针的任意成员变量,在方法结束后,修改都是有效的。
这种方式就十分接近于其他语言中面向对象中的 this 或者 self 。
例如我们为 Person 添加一个 SetAge 方法,来修改实例变量的年龄。
3、 指针类型接收者 使用时机
注:并不是所有情况下都希望修改数据
1、需要修改接收者中的值
2、接收者是拷贝代价比较大的大对象
3、保证一致性,如果有某个方法使用了指针接收者,那么其他的方法也应该使用指针接收者。