Go语言基础之结构体 - 李文周的博客
在Go语言中没有类的概念,也不支持封装、继承、多态等面向对象的概念
Go语言中通过结构体的内嵌再配合接口比面向对象具有更高的拓展性和灵活性
类型别名和自定义类型
自定义类型
虽然go
中已经内置了很多基本数据类型,但是我们也可以使用type
关键字来进行自定义特定的数据类型
//将MyInt定义为int类型
type MyInt int
通过type
关键字的定义,MyInt
就是一种新的数据类型,并拥有int
的特性
类型别名
顾名思义,就是给某个类型取一个别名
type TypeAlias = Type
类型别名只会在代码中显现,等代码编译完后还是显现为原本的类型
//类型定义
type NewInt int
//类型别名
type MyInt = int
func main() {
var a NewInt
var b MyInt
fmt.Printf("type of a:%T\n", a) //type of a:main.NewInt
fmt.Printf("type of b:%T\n", b) //type of b:int
}
结果显示a的类型是
main.NewInt
,表示main
包下定义的NewInt
类型。b的类型是int
。MyInt
类型只会在代码中存在,编译完成时并不会有MyInt
类型。
结构体
Go语言中的基础数据类型可以表示一些事物的基本属性,但是当我们想表达一个事物的全部或部分属性时,这时候再用单一的基本数据类型明显就无法满足需求了,Go语言提供了一种自定义数据类型,可以封装多个基本数据类型,这种数据类型叫结构体,英文名称struct
。 也就是我们可以通过struct
来定义自己的类型了。
在Go中,通过结构体来实现面向对象
结构体的定义
type 类型名 struct {
字段名 字段类型
字段名 字段类型
…
}
type person struct {
name string
city string
age int8
}
相同类型的属性也可以定义到一行
type person1 struct {
name, city string
age int8
}
语言内置的基础数据类型是用来描述一个值的,而结构体是用来描述一组值的。比如一个人有名字、年龄和居住城市等,本质上是一种聚合型的数据类型
结构体实例化
结构体本身就是我们自己定义的数据类型,因此可以也可以使用var
来进行声明
基本结构体
type person struct {
name string
city string
age int8
}
func main() {
var p1 person
p1.name = "沙河娜扎"
p1.city = "北京"
p1.age = 18
fmt.Printf("p1=%v\n", p1) //p1={沙河娜扎 北京 18}
fmt.Printf("p1=%#v\n", p1) //p1=main.person{name:"沙河娜扎", city:"北京", age:18}
}
匿名结构体
在定义一些临时数据类型的时候,可以使用匿名结构体
package main
import (
"fmt"
)
func main() {
var user struct{Name string; Age int}
user.Name = "小王子"
user.Age = 18
fmt.Printf("%#v\n", user)
}
指针类型结构体
var p2 = new(person)
fmt.Printf("%T\n", p2) //*main.person
fmt.Printf("p2=%#v\n", p2) //p2=&main.person{name:"", city:"", age:0}
可以对结构体指针使用.
来访问结构体的成员
var p2 = new(person)
p2.name = "小王子"
p2.age = 28
p2.city = "上海"
fmt.Printf("p2=%#v\n", p2) //p2=&main.person{name:"小王子", city:"上海", age:28}
p3.name = "七米"
其实在底层是(*p3).name = "七米"
,这是Go语言帮我们实现的语法糖。
结构体初始化
没有初始化的结构体,其成员变量都是对应类型的零值
type person struct {
name string
city string
age int8
}
func main() {
var p4 person
fmt.Printf("p4=%#v\n", p4) //p4=main.person{name:"", city:"", age:0}
}
使用键值对初始化
使用键值对对结构体进行初始化时,键对应结构体的字段,值对应该字段的初始值。
p5 := person{
name: "小王子",
city: "北京",
age: 18,
}
fmt.Printf("p5=%#v\n", p5) //p5=main.person{name:"小王子", city:"北京", age:18}
某些字段没有初始值的时候,就是对应的类型的零值
p7 := &person{
city: "北京",
}
fmt.Printf("p7=%#v\n", p7) //p7=&main.person{name:"", city:"北京", age:0}
使用值的列表进行初始化
初始化结构体的时候可以简写,也就是初始化的时候不写键,直接写值:
p8 := &person{
"沙河娜扎",
"北京",
28,
}
fmt.Printf("p8=%#v\n", p8) //p8=&main.person{name:"沙河娜扎", city:"北京", age:28}
使用这种格式初始化时,需要注意:
- 必须初始化结构体的所有字段。
- 初始值的填充顺序必须与字段在结构体中的声明顺序一致。
- 该方式不能和键值初始化方式混用。
构造函数
Go语言的结构体没有构造函数,我们可以自己实现。 例如,下方的代码就实现了一个person
的构造函数。 因为struct
是值类型,如果结构体比较复杂的话,值拷贝性能开销会比较大,所以该构造函数返回的是结构体指针类型。func newPerson(name, city string, age int8) *person {
return &person{
name: name,
city: city,
age: age,
}
}
方法和接收者
不同于普通的函数,go语言中的方法只能作用于某一种特定类型的变量
这种特定类型的变量叫做接收者
接受者的作用有点类似于其他语言中的this
或者self
方法的定义如下:
func (接收者变量 接收者类型) 方法名(参数列表) (返回参数) {
函数体
}
其中,
- 接收者变量:接收者中的参数变量名在命名时,官方建议使用接收者类型名称首字母的小写,而不是self、this之类的命名。例如,Person类型的接收者变量应该命名为 p,Connector类型的接收者变量应该命名为c等。
- 接收者类型:接收者类型和参数类似,可以是指针类型和非指针类型。
- 方法名、参数列表、返回参数:具体格式与函数定义相同。 ```go //Person 结构体 type Person struct { name string age int8 }
//NewPerson 构造函数 func NewPerson(name string, age int8) *Person { return &Person{ name: name, age: age, } }
//Dream Person做梦的方法 func (p Person) Dream() { fmt.Printf(“%s的梦想是学好Go语言!\n”, p.name) }
func main() { p1 := NewPerson(“小王子”, 25) p1.Dream() } ```
也就是说在Go语言中,如果需要实现某个类中的方法,那么就需要使用接收者来定义方法的内容