这是我参与「第五届青训营 」伴学笔记创作活动的第 5 天
1. Go语言中的面向对象
- Go语言中没有Class类的概念,但是有struct结构体的概念
- 虽然Go语言没有Class,但是并不意味着Go不能面向对象编程,毕竟面向对象编程只是一个概念
2. 封装
1. 属性
- struct结构体中定义的变量就可以视为属性、字段
2. 方法
- struct结构体中不能定义方法
- Go语言中的方法是作用在接收者上的一个函数,接收者是某种特殊类型的变量。所以Go语言中的方法是特殊类型的函数
- 语法:
func (recv recv_type) func_name([parameter list]) ([return list]) - 举一个栗子
// 1. 定义一个三角形类
type Triangle struct {
Bottom float32
Height float32
}
// 2. 定义三角形类计算面积的方法Area()
func (t *Triangle) Area() (area float32) {
area = 0.5 * t.Bottom * t.Height
}
func main() {
// 1. 创建对象,或者叫结构体变量
r := Triangle{1, 2}
// 2. 调用对象的方法获取面积
fmt.Println(r.Area())
}
3. 访问权限
- Go语言中没有public这类的权限访问修饰符,而是使用首字母大小写来进行判断
- Go语言中的常量、变量、类型、接口、函数、结构体等命名开头是大写的话就表示是公开的,可以被其它包调用,非大写的就只能在包内访问
- 对于小写字母开头的私有类型,Go语言中没有提供get或set方法,我们只能自己定义,通常情况下get方法使用Get开头,set方法使用set方法
package person
type Student struct {
name String
age int
}
// 定义get方法,获取私有类型值
func (s *Student) GetName() string {
return s.name
}
// 定义set方法,设置私有属性值
func (s *Student) SetName(name string) {
s.name = name
}
// --------------------------------------------
package main
import "person"
func main() {
s := new(person.Student)
s.SetName("Tom")
fmt.Println(s.GetName())
}
// 这只是一条测试语句
4. toString()函数
- Go语言中也有类似于Java中的toString()函数,只需要定义String() string方法,就可以以想要的形式打印输出结果,比如说time包中的Month
3. 继承
- Go语言中没有extends关键字,而是在结构体中内嵌匿名类型的方法来实现继承,下面举一个简单的栗子
// 1. 定义一个引擎接口
type Engine interface {
Run() // 第一个方法
Stop() // 第二个方法
}
// 2. 定义一个Bus类型
type Bus struct {
Engine // 内嵌接口
}
// 3. 定义结构体的方法,可以在方法中使用接口类型中定义的方法
// 这个时候代码是跑不起来的,需要额外实现这几个方法
func (c *Bus) Working() {
c.Run()
c.Stop()
}
4. 多态
- Go语言中的多态只需要让不同的结构体实现相同接口中的方法即可,下面举个例子
package main
import "fmt"
type Shape interface {
Area() float32
}
type Square struct {
side float32
}
func (s *Square) Area() float32 {
return s.side * s.side
}
type Triangle struct {
Bottom float32
Height float32
}
func (t *Triangle) Area() float32 {
return (t.Bottom * t.Height) / 2
}
func main() {
s := &Square{10}
t := &Triangle{3, 4}
shapes := []Shape{s, t}
for n, _ := range shapes {
fmt.Printf("类型为:%T\n", shapes[n])
fmt.Printf("面积为:%v\n", shapes[n].Area())
}
}
5. 接口
1. 什么是接口
- 接口类型是Go语言中最重要的特性之一,接口类型定义了一组方法,但只是定义了方法,并没有给出方法的具体实现
- 接口本质上是一种类型,确切的说,是指针类型
- Go语言会根据非指针成员方法自动生成一个指针成员方法
- 但凡有一个指针成员方法,给接口赋值的时候就得赋值实例对象的指针(地址)
2. 接口的赋值
- Go语言中的接口不支持实例化操作,但是支持赋值操作,有如下两种
-
- 将实现接口的对象或者对象指针赋值给接口
-
-
- 如果接口中全部是非指针成员方法,就可以将结构体对象赋值给接口,但只要接口中有一个指针成员方法,就要将结构体对象的指针赋值给接口,因为指针成员方法的接受者是指针变量,所以只有指针对象才能调用,Go语言会自动根据非指针成员方法生成对应的指针成员方法
- 这种要求实例类对象所属的类(结构体)要实现接口中的所有方法
- Go语言规定了不能给内置的数据类型定义方法,但是我们可以使用内置的类型定义自己的类型,然后给自己的类型添加方法,下面举个简单的栗子:
-
type Integer int
type NumI interface {
add(Integer) Integer
}
func (a Integer) add(b Integer) (res Integer) {
res = a + b
return
}
func main() {
var a NumI
var b Integer = 1
a = b
fmt.Println(a.add(1))
}
-
- 将一个接口赋值给另一个接口
-
-
- 在Go语言中,只要两个接口拥有相同的方法列表(不管这些方法的顺序是怎么样的),它们就是等同的,就可以相互赋值了
- Go语言中并不强制要求接口赋值给接口的时候接口要完全等价,比如A接口中的方法列表是B接口方法列表的子集,那么就可以将B接口赋值给A接口,注意顺序不要弄反了,是将大的赋值给小的这样才能完全覆盖
-
3. 接口的查询
- 接口查询是在程序动态运行的时候进行的
- 语法:
接口名.(类型)
4. 接口的组合
- 不仅结构体可以和结构体组合,接口与接口之间也可以组合,下面举个简单的栗子
// 1. 定义第一个接口
type Interface1 interface {
a() int
b() int
}
// 2. 定义第二个接口
type Interface2 interface {
c() int
d() int
}
// 3. 定义一个接口然后组合这些interface1和interface2
// 实际上这种就是将方法提前组合了一下
type Interface3 interface {
Interface1
Interface2
}
5. 接口的常见应用
1. 类型推断
package main
import "fmt"
func main() {
var a interface{} = func(a int) string {
return fmt.Sprintf("d: %d", a)
}
switch b := a.(type) { // 局部变量b是类型转换后的结果
case nil:
fmt.Println("这是一个空指针!")
case *int:
fmt.Println(*b)
case func(int) string:
fmt.Println(b(66))
default:
fmt.Println("这是默认的情况")
}
}