一、struct简介
go语言中没有像类的概念,但是可以通过结构体struct实现oop(面向对象编程)。struct的成员(也叫属性或字段)可以是任何类型,如普通类型、复合类型、函数、map、interface、struct等,所以我们可以理解为go语言中的“类”。
二、struct详解
定义 初始化与申明
在定义struct成员时候区分大小写,若首字母大写则该成员为公有成员(对外可见),否则是私有成员(对外不可见)。
package main
import "fmt"
func main() {
// 声明与初始化
var p1 Person
var p2 *Person = &Person{}
var p3 *Person = new(Person)
fmt.Println(p1, p2, p3) // { 0 false} &{ 0 false} &{ 0 false}
}
// Person 人类
type Person struct {
name string
age int
sex bool
}
struct使用
在struct中,无论使用的是指针的方式声明还是普通方式,访问其成员都使用”.”,在访问的时候编译器会自动把 stu2.name 转为 (*stu2).name。
struct分配内存使用new,返回的是指针。
struct没有构造函数,但是我们可以自己定义“构造函数”。
struct是我们自己定义的类型,不能和其他类型进行强制转换。
package main
import "fmt"
func main() {
// 声明与初始化
var p1 Person
p1.name = "张三"
p1.age = 23
fmt.Println(p1.name) //张三
var p2 *Person = new(Person)
p2.name = "李四"
p2.age = 24
fmt.Println(p1.name, (*p2).name) //张三 李四
var p3 *Person = &Person{
name: "王五",
age: 55,
}
fmt.Println(p1, p2, p3) // {张三 23} &{李四 24} &{王五 55}
}
// Person 人类
type Person struct {
name string
age int
}
自定义构造函数
以下是通过工厂模式自定义构造函数方法
package main
import "fmt"
func main() {
// 声明与初始化
person := newPerson("小三", 22)
fmt.Println(person) //&{小三 22}
}
// Person 人类
type Person struct {
name string
age int
}
func newPerson(name string, age int) *Person {
return &Person{
name: name,
age: age,
}
}
TAG 标记
tag可以为结构体的成员添加说明或者标签便于使用,这些说明可以通过反射获取到。
在前面提到了,结构体中的成员首字母小写对外不可见,但是我们把成员定义为首字母大写这样与外界进行数据交互会带来极大的不便,此时tag带来了解决方法。
package main
import (
"encoding/json"
"fmt"
)
func main() {
// 声明与初始化
person := Person{
Name: "小三",
Age: 22,
}
data, err := json.Marshal(person)
if err != nil {
fmt.Println("json encode failed err:", err)
return
}
fmt.Println(string(data)) // {"namess":"小三","age":22}
}
// Person 人类
type Person struct {
Name string `json:"name"`
Age int `json:"age"`
}
匿名成员(字段、属性)
结构体中,每个成员不一定都有名称,也允许字段没有名字,即匿名成员。
匿名成员的一个重要作用,可以用来实现oop中的继承。
同一种类型匿名成员只允许最多存在一个。
当匿名成员是结构体时,且两个结构体中都存在相同字段时,优先选择最近的字段。
package main
import (
"fmt"
)
func main() {
// 声明与初始化
stu := new(Student)
stu.Age = 22 // 优先选择Student中的Age
fmt.Println(stu.Person.Age, stu.Age) // 0 22
}
// Person 人类
type Person struct {
Name string `json:"name"`
Age int `json:"age"`
}
// Student 学生
type Student struct {
score string
Age int
Person
}
继承、多继承
当结构体中的成员也是结构体时,该结构体就继承了这个结构体,继承了其所有的方法与属性,当然有多个结构体成员也就是多继承。
访问父结构中属性也使用“.”,但是当子结构体中存在和父结构中的字段相同时候,只能使用:”子结构体.父结构体.字段”访问父结构体中的属性,如上面示例的stu.Person.Age
继承结构体可以使用别名,访问的时候通过别名访问,如下面示例man.job.Salary:
package main
import (
"encoding/json"
"fmt"
)
func main() {
var man = new(Man)
man.Name = "老九"
man.Sex = "男"
man.Age = 22
man.Job.Salary = 8500
data, err := json.Marshal(man)
if err != nil {
fmt.Println("json encode failed err:", err)
return
}
fmt.Println(string(data)) // {"Sex":"男","Job":{"Salary":8500,"Classes":""},"name":"老九","age":22}
}
// Person 人类
type Person struct {
Name string `json:"name"`
Age int `json:"age"`
}
// Teacher 老师
type Teacher struct {
Salary int
Classes string
}
// Man 男人
type Man struct {
Sex string
Job Teacher //别名,继承Teacher
Person //继承Person
}
结构体中的方法
go语言中的方法是作用在特定类型的变量上,因此自定义的类型都可以有方法,不仅仅是在结构体中。
go中的方法和传统的类的方法不太一样,方法和类并非组织在一起,传统的oop方法和类放在一个文件里面,而go语言只要在同一个包里就可,可分散在不同文件里。go的理念就是数据和实现分离,引用官方说法:“Methods are not mixed with the data definition (the structs): they are orthogonal to types; representation(data) and behavior (methods) are independent”
方法的调用通过recv.methodName(),其访问控制也是通过大小写区分。
方法定义,其中recv代表方法作用的结构体:
func (recv type) methodName(parameter_list) (return_value_list) { … }
package main
import (
"fmt"
)
func main() {
var man = new(Person)
man.Name = "老九"
man.Age = 22
man.Getname()
}
// Person 人类
type Person struct {
Name string `json:"name"`
Age int `json:"age"`
}
// Getname 取得名字
func (p Person) Getname() string { //p代表结构体本身的实列,类似python中的self,这里p可以写为 self
fmt.Println(p.Name)
return p.Name
}