Golang 结构体

优质
小牛编辑
140浏览
2023-12-01

Go 语言中的结构体和 C 语言中结构体一样,都是用来保存一组 不同类型的数据

Go 语言中的结构体和 C 语言中结构体一样,都需要先定义结构体类型再利用结构体类型定义结构体变量

定义结构体类型

type 类型名称 struct{
  属性名称 属性类型
  属性名称 属性类型
  ... ...
}
type Studentstruct {
	name string
	age int
}

创建结构体变量的两种方式

方式一:先定义结构体类型,再定义结构体变量

和 C 语言中的结构体一样,如果结构体类型需要多次使用,那么建议先定义类型再定义变量

package main
import "fmt"
func main() {
  type Student struct {
  	name string
  	age int
  }	

  // 完全初始化
  var stu1= Student{"lnj", 33}
  fmt.Println(stu1)
  // 部分初始化
  // 部分初始化必须通过 属性名称: 方式指定要初始化的属性
  var stu2 = Student{name:"lnj"}
  fmt.Println(stu2)
}

方式二:定义结构体类型同时定义结构体变量(匿名结构体)

和C语言中的结构体一样,如果结构体类型只需要使用一次,那么建议定义类型同时定义变量

package main
import "fmt"
func main() {
  // 注意: 这里不用写type和结构体类型名称
  var stu2 = struct {
  	name string
  	age int
  }{
  	name: "lnj",
  	age: 33,
  }
  fmt.Println(stu2)
}

结构体类型操作

  package main
  import "fmt"
  type Student struct {
	  name string
	  age int
  }
  func main() {
	var stu= Student{"lnj", 33}
	// 获取结构体中某个属性对应的值
	fmt.Println(stu.name)

	// 修改结构体中某个属性对应的值
	stu.name = "zs"
	fmt.Println(stu)
  }

和 slice、map 不同的是,只要定义了结构体变量就可以使用结构体变量,默认情况下结构体中的所有属性都是属性对应类型的默认值

  package main
  import "fmt"
  type Student struct {
	  name string
	  age int
  }
  func main() {
	var stu Student // 相当于 var stu = Student{}
	fmt.Println(stu) // { 0}
	stu.name = "lnj" // 不会报错
	stu.age = 33
	fmt.Println(stu) // {lnj 33}
  }

复杂结构体成员

创建时可以按照属性单独存在时初始化方式初始化

package main
import "fmt"
type Student struct {
    name string
    age int
}
func main() {
  type Demo struct {
  	age int // 基本类型作为属性
  	arr [3]int // 数组类型作为属性
  	sce []int // 切片类型作为属性
  	mp map[string]string // 字典类型作为属性
  	stu Student // 结构体类型作为属性
  }

  var d Demo = Demo{
  	33,
  	[3]int{1, 3, 5},
  	[]int{2, 4, 6},
  	map[string]string{"class":"one"},
  	Student{
  		"lnj",
  		33,
  	},
  }
  fmt.Println(d) // {33 [1 3 5] [2 4 6] map[class:one] {lnj 33}}
}

slice、map 类型属性默认值是 nil,不能直接使用

package main
import "fmt"
type Student struct {
    name string
    age int
}
func main() {
  type Demo struct {
  	age int // 基本类型作为属性
  	arr [3]int // 数组类型作为属性
  	sce []int // 切片类型作为属性
  	mp map[string]string // 字典类型作为属性
  	stu Student // 结构体类型作为属性
  }

  // 定义结构体变量
  var d Demo
  // 可以直接使用基本类型属性
  d.age = 33
  // 可以直接使用数组类型属性
  d.arr[0] = 666
  // 不可以直接使用切片类型属性
  //d.sce[0] = 888 // 编译报错
  d.sce = make([]int, 5) // 先创建
  d.sce[0] = 888 // 后使用
  // 不可以直接使用字典类型属性
  //d.mp["class"] = "one" // 编译报错
  d.mp = make(map[string]string)// 先创建
  d.mp["class"] = "one"// 后使用
  // 可以直接使用结构体类型属性
  d.stu.name = "lnj"
  fmt.Println(d) // {33 [666 0 0] [888 0 0 0 0] map[class:one] {lnj 0}}
}

结构体类型转换

  • 属性名、属性类型、属性个数、排列顺序都是类型组成部分
  • 只有属性名、属性类型、属性个数、排列顺序都相同的结构体类型才能转换
  package main
  import "fmt"
  func main() {
	type Person1 struct {
		name string
		age int
	}
	type Person2 struct {
		name string
		age int
	}
	type Person3 struct {
		age int
		name string
	}
	type Person4 struct {
		nm string
		age int
	}
	type Person5 struct {
		name string
		age string
	}
	type Person6 struct {
		age int
		name string
		gender string
	}

	var p1 Person1 = Person1{"lnj", 33}
	var p2 Person2
	// 类型名称不一样不能直接赋值(Person1、Person2)
	//p2 = p1

	// 虽然类型名称不一样, 但是两个类型中的`属性名称`、`属性类型`、`属性个数`、`排列顺序`都一样,所以可以强制转换
	p2 = Person2(p1)
	fmt.Println(p2)
	// 两个结构体类型中的`属性名称`、`属性类型`、`属性个数`都一样,但是`排列顺序`不一样,所以不能强制转换
	//var p3 Person3
	//p3 = Person3(p1)
	//fmt.Println(p3)

	// 两个结构体类型中的`属性类型`、`属性个数`、`排列顺序`都一样,但是`属性名称`不一样,所以不能强制转换
	//var p4 Person4
	//p4 = Person4(p1)
	//fmt.Println(p4)

	// 两个结构体类型中的`属性名称`、`属性个数`、`排列顺序`都一样,但是`属性类型`不一样,所以不能强制转换
	//var p5 Person5
	//p5 = Person5(p1)
	//fmt.Println(p5)

	// 两个结构体类型中的`属性名称`、`属性类型`、`排列顺序`都一样,但是`属性个数`不一样,所以不能强制转换
	//var p6 Person6
	//p6 = Person6(p1)
	//fmt.Println(p6)
  }

匿名属性

  • 没有指定属性名称,只有属性的类型,我们称之为匿名属性
  • 任何 有命名的数据类型 都可以作为匿名属性(int、float、bool、string、struct 等)
package main
import "fmt"
func main() {
  type Person struct {
  	int
  	float32
  	bool
  	string
  }
  // 不指定名称初始化
  per1 := Person{3, 3.14, false, "lnj"}
  fmt.Println(per1)

  // 可以把数据类型作为名字显示初始化
  per2 := Person{
  	int: 3,
  	float32: 3.14,
  	bool: true,
  	string: "lnj",
  }
  fmt.Println(per2)

  // 可以把数据类型当做属性名称操作结构体
  per2.int = 666
  fmt.Println(per2.int) // 666
}

Go 语言中最常见的匿名属性是用 结构体类型作为匿名属性

package main
import "fmt"
func main() {
 type Person struct {
 	name string
 	age int
 }
 type Student struct {
 	Person // 匿名属性
 	class string
 }

 stu := Student{
 	Person{"lnj", 33},
 	"学前一班",
 }
 fmt.Println(stu) // {{lnj 33} 学前一班}
}

如果结构体作为匿名属性,想访问匿名属性的属性有两种方式

package main
import "fmt"
func main() {
  type Person struct {
  	name string
  	age int
  }
  type Student struct {
  	Person // 匿名属性
  	class string
  }

  stu := Student{
  	Person{"lnj", 33},
  	"学前一班",
  }
  fmt.Println(stu) // {{lnj 33} 学前一班}

  // 方式一: 先找到匿名属性,再访问匿名属性中的属性
  stu.Person.name = "zs"
  fmt.Println(stu) // {{zs 33} 学前一班}
  // 方式二: 直接访问匿名属性中的属性
  // 系统会先查找当前结构体有没有名称叫做name的属性
  // 如果没有会继续查找匿名属性中有没有名称叫做name的属性
  stu.name = "ww"
  fmt.Println(stu) // {{ww 33} 学前一班}
}

注意点:如果多个匿名属性的属性名称相同,那么不能通过方式二操作,只能通过方式一

package main
import "fmt"
func main() {
  type Person struct {
  	name string
  	age int
  }
  type Class struct {
  	name string
  	time string
  }
  type Student struct {
  	Person // 匿名属性
  	Class // 匿名属性
  }
  stu := Student{
  	Person{"lnj", 33},
  	Class{"学前一班", "2020-12-12"},
  }
  fmt.Println(stu) // {{lnj 33} {学前一班 2020-12-12}}
  // 编译报错, 系统搞不清楚要找哪个name
  //stu.name = "zs"

  stu.Person.name = "zs"
  stu.Class.name = "小学一年级"
  fmt.Println(stu) // {{zs 33} {小学一年级 2020-12-12}}
}

注意点:只有匿名结构体才支持向上查找

package main
import "fmt"
func main() {
  type Person struct {
  	name string
  }
  type Student struct {
  	per Person
  	age int
  }

  var stu Student = Student{Person{"lnj"}, 18}
  //fmt.Println(stu.name) // 报错
  fmt.Println(stu.per.name) // 必须通过属性进一步查找
  fmt.Println(stu.age)
}

注意点:如果匿名属性是一个结构体类型,那么这个结构体类型不能是自己

package main
import "fmt"
func main() {
  type Person struct {
  	Person // 错误
  	name string
  }
  type Student struct {
  	*Student  // 正确, 链表
  	age int
  }
}