Go语言面向对象

编程 · 02-22 · 142 人浏览

Go不是纯粹的面向对象语言,和传统的面向对象编程有区别,所以说Go语言支持面向对象编程特性是比较准确的。

Go语言基于结构体struct来实现OOP特性,通过type和struct关键字声明结构体,格式如下:

type 类型名 struct { // 类型名在同一个包内不能重复
  字段1 字段1类型 // 字段名必须唯一
  字段2 字段2类型
}

结构体从本质上讲是一种自定义的数据类型,只不过这种数据类型比较复杂,可以认为结构体是一种聚合类型。

结构体实例化

声明结构体变量再赋值:

type Student struct {
  sid    int      // 字段首字母小写,类似private,其它包不能使用
  course []string // 选修课程
}

func main() {
  // 声明一个结构体对象,值类型,默认开辟空间,字段赋予零值
  var s Student
  fmt.Println("s:", s) // s: {0 []}
  // 访问结构体成员使用点号 . 操作符
  fmt.Println(s.sid) // 0
  // 更改成员变量值
  s.sid = 1001
  fmt.Println(s.sid) // 1001

  // 方式2:键值对赋值
  s2 := Student{sid: 1002, course: []string{"chinese", "math"}}
  fmt.Println(s2)
  // 方式3:多值赋值
  s3 := Student{1003, []string{"chinese", "math"}}
  fmt.Println(s3)
}

实例化之&结构体:

func initSid(s *Student) {
  s.sid = 1001 // 其实是(*s).sid = 1001
}

func main() {
  s := Student{course: []string{"chinese", "math"}}
  initSid(&s)
  fmt.Println(s) // {1001 [chinese math]}

  s2 := &Student{course: []string{"chinese", "math"}}
  initSid(s2)
  fmt.Println(s2) // &{1001 [chinese math]}
}

实例化之new(结构体):

func main() {
  s := new(Student) // 等同于 &Student{}
  fmt.Println(s)    // &{0 []}
}

模拟构造函数

Go语言没有构造函数,但可以使用结构体初始化过程来模拟实现构造函数(首字母大写)。

// 自定义工厂函数,返回局部变量地址
func NewStudent(sid int, course []string) *Student {
  return &Student{
    sid:    sid,
    course: course}
}

func main() {
  s := NewStudent(1001, []string{"math", "english"})
  fmt.Println(s) // &{1001 [math english]}
}

方法接收器

Go语言中的方法(Method)是一种作用于特定类型变量的函数,这种特定类型变量叫做接收者(Receiver)。方法与函数的区别是,函数不属于任何类型,方法属于特定类型。

func (s Student) learn() {
  fmt.Printf("%d 正在学习...", s.sid)
}

func main() {
  s := NewStudent(1001, []string{"math", "english"})
  s.learn()
}

接收者是指针类型:

type Player struct {
  Name        string
  HealthPoint int
}

func NewPlayer(name string, hp int) *Player {
  return &Player{
    name,
    hp,
  }
}

func (p *Player) attack() {
  fmt.Printf("%s 发起攻击!\n", p.Name)
}

func (p *Player) attacked() {
  fmt.Printf("%s 被攻击!\n", p.Name)
  p.HealthPoint -= 10
  fmt.Println("HealthPoint:", p.HealthPoint)
}

func main() {
  player := NewPlayer("张三", 100)
  player.attack()
  player.attacked()
}

匿名字段

结构体允许其成员字段在声明时没有字段名而只有类型,这种没有名字的字段称为匿名字段。

type Person struct {
  string // 其实是string string
  int    // 其实是int int
}

func main() {
  p := Person{
    "张三",
    18,
  }
  fmt.Println(p.string, p.int) // 张三 18
}

结构体也可以作为匿名字段使用:

type Addr struct {
  province string
  city     string
}

type Person struct {
  string
  int
  Addr // 其实是Addr Addr
}

func main() {
  p := Person{
    "张三",
    18,
    Addr{"广东", "深圳"},
  }
  fmt.Println(p.Addr.province, p.city) // 广东 深圳
}

当结构体中有和匿名字段相同字段时,采用外层优先访问原则。

包和封装

每个目录一个包,main包包含可执行入口。

为结构定义的方法必须放在同一个包内,可以是不同文件。

结构体继承

// Animal 动物
type Animal struct {
  name string
}

func (a *Animal) eat() {
  fmt.Printf("%s is eating!\n", a.name)
}

// Dog 类型
type Dog struct {
  Kind    string
  *Animal // 通过嵌套匿名结构体实现继承,直接写类型,没有字段名
}

func (d *Dog) bark() {
  fmt.Printf("%s is barking ~\n", d.name)
}

func main() {
  d := &Dog{
    Kind: "金毛",
    Animal: &Animal{ // 注意嵌套的是结构体指针
      name: "旺财",
    },
  }
  d.eat()
  d.bark()
}

结构体标签

type Stu struct {
  Name string `json:"name"` // 结构体标签
  Age  int    `json:"-"`    // 表示不参与序列化
}

func main() {
  stuStruct := Stu{Name: "张三", Age: 18}

  // 序列化
  jsonStuStruct, _ := json.Marshal(stuStruct)
  fmt.Println(string(jsonStuStruct)) // {"name":"张三"}

  // 反序列化
  var StuStruct Stu
  err := json.Unmarshal(jsonStuStruct, &StuStruct)
  if err != nil {
    return
  }
  fmt.Println(StuStruct) // {张三 0}
}
Go
Theme Jasmine by Kent Liao