马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有账号?立即注册
×
原文链接:Golang底子笔记九之方法与接口
本篇笔记介绍 Golang 里方法和接口,以下是本篇笔记目录:
1、方法
首先介绍一下方法。
方法是与特定范例关联的函数,我们在实现一个函数前,绑定一个范例,就实现了这个范例的方法。
比如我们想实现一个结构体的方法,可以如下操作:- type Person struct {
- Name string
- Age int
- }
- func (person Person) fmtPersonInfo() {
- fmt.Printf("person name is %s, age is %d\n", person.Name, person.Age)
- }
复制代码 在上面的操作中,我们就为 Person 这个结构体绑定了一个方法,而其调用也很简单,就是实例化一个 Person 结构体后,就可以对其进行调用:- person := Person{Name: "Hunter", Age: 28}
- person.fmtPersonInfo()
复制代码 方法支持的范例
方法支持绑定的范例有结构体、指针范例、接口范例以及自定义范例,但是不支持绑定 Golang 内置的范例包括 int、slice、map 等。
方法绑定到指针范例
前面介绍了方法绑定到结构体上,这里再介绍一个绑定到指针范例上,照旧前面的 Person 结构体,绑定到其指针上,来实现更改 Age 字段的操作:- func (person *Person) ChangeAge(age int) { person.Age = age}person := Person{Name: "Hunter", Age: 28}
- person.fmtPersonInfo()person.ChangeAge(18)person.fmtPersonInfo()
复制代码 第二次打印信息就可以看到 person 的 age 已经发生了变革。
这里必要注意一点,Person 结构体是值范例,假如绑定的是其结构体自己,而非其指针范例,在方法中对其更改后,并不会影响结构体自己,比如下面的操作:- func (person Person) NewChangeAge(age int) { person.Age = age}person := Person{Name: "Hunter", Age: 28}
- person.fmtPersonInfo()person.NewChangeAge(18)person.fmtPersonInfo()
复制代码 可以看到,这里调用 NewChangeAge() 方法后,没有对 person 这个结构体自己进行更改。
方法绑定到自定义范例
而假如想要绑定 int、slice、map 等内置范例,可以通过方法支持的自定义范例来绑定。
比如我们想要实现利用 slice 绑定一个打印其长度的方法,可以通过自定义范例设置一个别名,通过别名来绑定一个方法:- type MySlce []int
- func (mySlice MySlce) printSliceLength() {
- fmt.Printf("mySliceLength is %d\n", len(mySlice))
- }
- slice := MySlce{1, 2, 3}
- slice.printSliceLength()
复制代码 2、接口
1. 接口的定义和实现
接口是一组方法签名的聚集,任何范例只要实现了接口中的所有方法,就被认为实现了该接口。
比如下面我们定义了一个形状的接口,内部有面积和周长两个空方法:- type Shape interface {
- Area() float64
- Perimeter() float64
- }
复制代码 这样我们就定义了一个接口。
而假如我们要实现这个接口,只必要实现这个接口里的两个方法 Area() 和 Perimeter() 就是实现了这个接口,这个过程是隐式的,不必要显式声明大概绑定。
接下来我们定义 Rectangle 和 Circle 两个结构体,并且实现 Area() 和 Perimeter() 两个方法:- type Rectangle struct {
- Width, Height float64
- }
- func (r Rectangle) Area() float64 {
- return r.Width * r.Height
- }
- func (r Rectangle) Perimeter() float64 {
- return 2 * (r.Width + r.Height)
- }
- type Circle struct {
- radius float64
- }
- func (c Circle) Area() float64 {
- return 3.14 * c.radius * c.radius
- }
- func (c Circle) Perimeter() float64 {
- return 2 * 3.14 * c.radius
- }
复制代码 我们已经分别用 Rectangle 和 Circle 这两个结构体实现了 Shape 接口。
那么这个接口在这里有什么作用呢,我们可以实现一个函数,接收接口范例的参数,那么实现了这个接口的结构体都可以作为传入:- func PrintShapeInfo(s Shape) {
- fmt.Println("Area: ", s.Area())
- fmt.Println("Perimeter: ", s.Perimeter())
- }
- func main() {
- r := Rectangle{Width: 2, Height: 3}
- PrintShapeInfo(r)
- c := Circle{Radius: 5}
- PrintShapeInfo(c)
- }
复制代码 2. 范例断言
范例断言用于检查接口值的底层具体范例,并提取该范例的值,其用法示例如下:- value, ok := interfaceValue.(ConcreteType)
复制代码
- value 是转换后的具体范例值
- ok 是一个布尔值,表现是否断言成功
- interfaceValue 是接口范例的变量
- ConcreteType 是目标具体范例。
比如我们可以修改 PrintShapeInfo 函数,在内部对其进行范例断言:
- func PrintShapeInfo(s Shape) {
- circle, ok := s.(Circle)
- if ok {
- fmt.Println("ths shape is circle, the area is: ", circle.Area())
- } else {
- fmt.Println("this shape is not circle")
- }
- fmt.Println("Area: ", s.Area())
- fmt.Println("Perimeter: ", s.Perimeter())
- }
复制代码 3. 空接口
空接口(interface{}) 可以表现任何范例的值,常用于处理不确定范例的数据。
比如我们想打印一个输入的变量,但是这个变量的范例不确定,我们可以利用空接口来处理这种情况。- func PrintType(a interface{}) {
- switch v := a.(type) {
- case int:
- fmt.Println("this is int: ", v)
- case float64:
- fmt.Println("this is float64: ", v)
- case string:
- fmt.Println("this is string: ", v)
- default:
- fmt.Println("this is other type: ", v)
- }
- }
- func main() {
- PrintType(1)
- PrintType(3.4)
- PrintType("abc")
- }
复制代码 3、用结构体实现类的功能
在 Golang 里没有类的相关定义,但是我们可以利用结构体和方法的组合来实现类的相关特性。
1. 封装
我们可以通过结构体字段的首字母大小写控制访问权限,然后提供公共方法来操作私有字段。
在结构体中,大写开头的字段为公开字段,小写开头的字段为私有字段。
我们用下面的示例来展示一下用结构体和方法来实现封装功能。
文件目录如下:- .
- ├── main.go
- ├── service
- │ └── person_operation.go
复制代码 其中 person_operation.go 的内容如下:- package service
- type Person struct {
- Name string
- Age int
- gender string
- }
- func (p *Person) SetGender(gender string) {
- p.gender = gender
- }
- func (p *Person) GetGender() string {
- return p.gender
- }
复制代码 其中,Person 这个结构体的 Name 和 Age 字段首字母都为大写,为公共字段,而 gender 首字母为小写,在 main.go 里不能直接引用,以是下面定义了两个公有接口提供设置和访问。
以下是 main.go 里的内容:- package main
- import (
- "fmt"
- "go_proj/service"
- )
- func main() {
- person := service.Person{
- Name: "张三",
- Age: 18,
- // gender: "男", // gender是私有属性,不能直接访问
- }
- // fmt.Println(person.gender) // gender是私有属性,不能直接访问
- fmt.Println(person.GetGender())
- person.SetGender("男")
- fmt.Println(person.GetGender())
- }
复制代码 在这里,gender 字段在 Person 定义和访问的时候都不能直接操作,必要通过设置的方法来进行定义以及访问。
2. 继承
我们可以通过结构体的嵌套来实现继承,比如下面新建一个 Chinese 结构体:- type Chinese struct {
- Person
- }
复制代码 然后我们定义的 Chinese 实例可以调用 Person 结构体的方法:- chinese := service.Chinese{
- Person: service.Person{
- Name: "张三",
- Age: 18,
- },
- }
- chinese.SetGender("男")
- fmt.Println(chinese.GetGender())
复制代码 3. 多态
多态则是同一方法名在不同的范例中有不同的实现,这个操作在前面介绍接口的就已经实现过了,这里不再做赘述。
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |