Go struct 结构体

发布时间: 更新时间: 总字数:5078 阅读时间:11m 作者: IP上海 分享 网址

Golang使用struct关键字声明结构体结构体是由一些列属性组成的复合数据类型,每个属性都有名称、类型组成。

介绍

  • struct 类型决定他有哪些方法,调用函数时即使 struct 对象为 nil 也不会报错
// 定义(命名)结构体
type 结构体名称 struct {
  属性1 类型
  属性2 类型

  // 匿名嵌入
  结构体
  *结构体

  // 命名嵌入
  属性3 结构体
  属性3 *结构体
}

// 声名
var p 结构体变量
var p *结构体变量

// 初始化后称为实例或对象
// 结构体零值初始化
var p 结构体变量 = 结构体名{}
// 结构体字面量初始化
var p 结构体变量 = 结构体名{值1,值2,...} // 值的顺序要和声明一致
var p 结构体变量 = 结构体名{
  属性1: 值1,
  属性2: 值2, // , 不能省略
}

// 指针初始化
var p *结构体变量 = new(结构体名) // nil
var p *结构体变量 = &结构体名{}
var p *结构体变量 = &结构体名{值1,值2,...} // 值的顺序要和声明一致
var p *结构体变量 = &结构体名{
  属性1: 值1,
  属性2: 值2, // , 不能省略
}

// 创建
func New结构体名(...) *结构体名{}

// 属性访问
结构体对象名/指针名.属性名
结构体对象名/指针名.方法名()

// 匿名结构体
var anony struct{
  属性1 类型
  属性2 类型
}
anony := struct{
  属性1 类型
  属性2 类型
}{}

// 匿名嵌套
变量名 := 结构体名{
  属性1: 值1,
  属性2: 值2, // , 不能省略
  匿名结构体名: 匿名结构体名{
    属性11: 值11,
    属性12: 值12,
  },
  匿名结构体名: &匿名结构体名{
    属性11: 值11,
    属性12: 值12,
  },
}

对象名.属性名
对象名.匿名嵌入结构体名.属性名
  • 结构体由 type 关键字定义,结构体中需要指明结构体的属性和属性类型,结构体的属性也可以结构体或结构体指针(分命名结构体和匿名结构体两种)。
  • 结构体使用 struct 标识,需要指定其包含的属性(属性和数据类型)
  • 声明结构体只需要定义变量类型为结构体,结构体默认的初始化值为该结构体中所有对应属性的零值,如 var user User
    • 结构体也可以被声明为指针(初始化值为 nil)。该声明变量称为该结构体的一个对象或实例
    • 也可以使用 new 函数对结构体进行初始化,返回一个指针对象

结构体的可见性

  • 结构体首字母大写则包对外可见(Public),否则不可见(Private)
  • 结构体属性首字母大写则包外可见(Public),否则包外不可见(Private)
  • 结构体可见性的优先级大于结构体属性可见性的优先级,即若结构体首字母小写,则不管结构体属性是否大写,对外均不可见
  • 特例:首字母大写的结构体,匿名嵌套首字母小写的结构体,若子结构体的属性是大写的,此时该子结构的属性是可以访问

type 关键字

Golang 使用 type 声明一种新的类型,格式如下:

type <Name> <Typper>

其中,Typper 可以是:内置数据类型、函数签名、结构体、接口等

示例如下:

package main

import "fmt"

// 声明新的数据类型
// 定义值类型
type Name string

// 定义引用类型
type City map[string]string

// 定义函数类型
type Callback func(...int) int

func ExampleType() {
	var name Name = "xianbin"
	fmt.Println(name, string(name) > "xianbin")

	c := make(City)
	c["name"] = "shanghai"
	c["no"] = "021"
	fmt.Printf("%T %#v\n", c, c)

	// 定义函数
	var addN Callback = func(args ...int) int {
		sum := 0
		for _, v := range args {
			sum += v
		}
		return sum
	}
	args := []int{1, 2, 3, 4, 5}
	sum := addN(args...)
	fmt.Println(sum)

	// Output:
	//xianbin false
	//main.City main.City{"name":"shanghai", "no":"021"}
	//15
}

struct 示例

基础使用示例

package main

import (
	"fmt"
	"time"
)

// 声明结构体
type City struct {
	Name    string
	No      string
	Age     int
	Created time.Time
}

func ExampleStruct() {
	var c1 City
	fmt.Printf("%T %#v\n", c1, c1)

	var c2 City = City{}
	fmt.Printf("%T %#v\n", c2, c2)

	// 字面量初始化
	// 顺序相关
	t, _ := time.Parse("2006-01-02 15:04:05", "1950-01-01 12:00:00")
	var shanghai City = City{"shanghai", "021", 50, t}
	fmt.Printf("%T %#v\n", shanghai, shanghai)

	// 可以无序
	shanghai = City{
		No:      "021",
		Name:    "shanghai",
		Age:     50,
		Created: t,
	}
	fmt.Printf("%T %#v\n", shanghai, shanghai)

	// 结构体指针
	// 初始化为 nil
	var c3 *City
	fmt.Printf("%T %#v\n", c3, c3)

	// 初始化为空 City
	c4 := &City{}
	fmt.Printf("%T %#v\n", c4, c4)

	// 初始化值
	c5 := &shanghai
	fmt.Printf("%T %#v\n", c5, c5)

	// new 初始化
	var c6 *City = new(City)
	fmt.Printf("%T %#v\n", c6, c6)

	// Output:
	//main.City main.City{Name:"", No:"", Age:0, Created:time.Date(1, time.January, 1, 0, 0, 0, 0, time.UTC)}
	//main.City main.City{Name:"", No:"", Age:0, Created:time.Date(1, time.January, 1, 0, 0, 0, 0, time.UTC)}
	//main.City main.City{Name:"shanghai", No:"021", Age:50, Created:time.Date(1950, time.January, 1, 12, 0, 0, 0, time.UTC)}
	//main.City main.City{Name:"shanghai", No:"021", Age:50, Created:time.Date(1950, time.January, 1, 12, 0, 0, 0, time.UTC)}
	//*main.City (*main.City)(nil)
	//*main.City &main.City{Name:"", No:"", Age:0, Created:time.Date(1, time.January, 1, 0, 0, 0, 0, time.UTC)}
	//*main.City &main.City{Name:"shanghai", No:"021", Age:50, Created:time.Date(1950, time.January, 1, 12, 0, 0, 0, time.UTC)}
	//*main.City &main.City{Name:"", No:"", Age:0, Created:time.Date(1, time.January, 1, 0, 0, 0, 0, time.UTC)}
}

结构体属性的访问和修改

package main

import (
	"fmt"
	"time"
)

// 声明结构体
type City struct {
	Name    string
	No      string
	Age     int
	Created time.Time
}

func ExampleStruct() {
	// 字面量初始化
	t, _ := time.Parse("2006-01-02 15:04:05", "1950-01-01 12:00:00")
	shanghai := City{
		No:      "021",
		Name:    "shanghai",
		Age:     50,
		Created: t,
	}
	// 访问属性
	fmt.Printf("%T %#v %d\n", shanghai.Name, shanghai.Name, shanghai.Age)

	// 更新属性
	shanghai.Age = 51
	fmt.Println(shanghai.Age)

	// 通过指针访问
	s2 := &shanghai
	s2.Age = 52 // 等同于 (*s2).Age = 52
	fmt.Println(shanghai.Age, s2.Age)

	// Output:
	//string "shanghai" 50
	//51
	//52 52
}

匿名结构体

在定义变量时,将类型定义为结构体,该场景称为匿名结构体。

使用场景:web 模板渲染;项目配置

package main

import (
	"fmt"
	"time"
)

func ExampleAnonyStruct() {
	// 匿名结构体
	var city struct {
		Name    string
		No      string
		Age     int
		Created time.Time
	}

	fmt.Printf("%T %#v\n", city, city)

	city.Name = "shanghai"
	fmt.Printf("%T %#v\n", city, city)

	c2 := struct {
		Name string
		No   string
		Age  int
	}{
		Name: "shanghai",
		No:   "021",
		Age:  50,
	}
	fmt.Printf("%T %#v\n", c2, c2)

	// Output:
	//struct { Name string; No string; Age int; Created time.Time } struct { Name string; No string; Age int; Created time.Time }{Name:"", No:"", Age:0, Created:time.Date(1, time.January, 1, 0, 0, 0, 0, time.UTC)}
	//struct { Name string; No string; Age int; Created time.Time } struct { Name string; No string; Age int; Created time.Time }{Name:"shanghai", No:"", Age:0, Created:time.Date(1, time.January, 1, 0, 0, 0, 0, time.UTC)}
	//struct { Name string; No string; Age int } struct { Name string; No string; Age int }{Name:"shanghai", No:"021", Age:50}
}

结构体命名嵌入

package main

import (
	"fmt"
)

// 声明结构体
type Addr struct {
	Region string
	street string
	No     string
}

type City struct {
	Name           string
	No             string
	Age            int
	GovernmentAddr Addr
}

func ExampleStruct() {
	// 嵌套结构体
	var shanghai = City{}
	fmt.Printf("%T %#v\n", shanghai, shanghai)

	addr := Addr{
		Region: "huangpu",
		street: "abc",
		No:     "0210001",
	}
	shanghai = City{
		Name:           "Shanghai",
		No:             "021",
		GovernmentAddr: addr,
	}
	fmt.Printf("%T %#v\n", shanghai, shanghai)

	shanghai = City{
		Name: "Shanghai",
		No:   "021",
		GovernmentAddr: Addr{
			Region: "huangpu",
			street: "abc",
			No:     "0210001",
		},
	}
	fmt.Printf("%T %#v\n", shanghai, shanghai)

	fmt.Println(shanghai.GovernmentAddr.Region)
	shanghai.GovernmentAddr.street = "henanlu"
	fmt.Printf("%T %#v\n", shanghai, shanghai)

	// Output:
	//main.City main.City{Name:"", No:"", Age:0, GovernmentAddr:main.Addr{Region:"", street:"", No:""}}
	//main.City main.City{Name:"Shanghai", No:"021", Age:0, GovernmentAddr:main.Addr{Region:"huangpu", street:"abc", No:"0210001"}}
	//main.City main.City{Name:"Shanghai", No:"021", Age:0, GovernmentAddr:main.Addr{Region:"huangpu", street:"abc", No:"0210001"}}
	//huangpu
	//main.City main.City{Name:"Shanghai", No:"021", Age:0, GovernmentAddr:main.Addr{Region:"huangpu", street:"henanlu", No:"0210001"}}
}

结构体匿名嵌入

结构体匿名嵌入是将已定义的结构体名称直接声明在新的结构体中,实现对新结构体的属性的扩展。

在结构体中,匿名嵌入的属性可以在访问是省略,直接 . 匿名嵌入的字段。

匿名嵌套和继承类似,但有最大的不同:

  • 访问父struct的属性时,先访问父struct是否有该属性,若没有访问子struct的属性
  • 若父struct和子struct存在相同的属性,它们是两个值
  • 若父struct有两个或两个以上的子stuct存在相同的属性,此时无法使用父struct . 属性获取该属性值,需使用子struct . 属性访问
package main

import "fmt"

// 声明结构体
type Addr struct {
	Region string
	street string
	No     string
}

// 地级城市
type City struct {
	Name           string
	No             string
	Age            int
  // 命名嵌入struct
	GovernmentAddr Addr
}

// 直辖市,匿名结构体,可以是结构体,也可以是结构体指针
type Municipality struct {
	City

	manager string
}

type Municipality2 struct {
	City

	Name    string
	manager string
}

func ExampleAnonymityStruct() {
	var mc Municipality
	fmt.Printf("%T %#v\n", mc, mc)

	// 初始化
	addr := Addr{
		Region: "huangpu",
		street: "abc",
		No:     "0210001",
	}
	shanghai := City{
		Name:           "Shanghai",
		No:             "021",
		GovernmentAddr: addr,
	}

	mc = Municipality{
		City:    shanghai,
		manager: "guowuyuan",
	}
	fmt.Printf("%T %#v\n", mc, mc)

	// 访问属性,City 可以省略
	fmt.Println(mc.Name, mc.City.Name, mc.GovernmentAddr.street)

	// 修改属性
	mc.GovernmentAddr.street = "xyz"
	fmt.Printf("%T %#v\n", mc, mc)

	// 两个 Name 的不同
	mc2 := Municipality2{
		City:    shanghai,
		Name:    "moudu",
		manager: "guowuyuan",
	}
	fmt.Printf("%T %#v\n", mc2, mc2)
	fmt.Println(mc2.Name, mc2.City.Name)

	// Output:
	//main.Municipality main.Municipality{City:main.City{Name:"", No:"", Age:0, GovernmentAddr:main.Addr{Region:"", street:"", No:""}}, manager:""}
	//main.Municipality main.Municipality{City:main.City{Name:"Shanghai", No:"021", Age:0, GovernmentAddr:main.Addr{Region:"huangpu", street:"abc", No:"0210001"}}, manager:"guowuyuan"}
	//Shanghai Shanghai abc
	//main.Municipality main.Municipality{City:main.City{Name:"Shanghai", No:"021", Age:0, GovernmentAddr:main.Addr{Region:"huangpu", street:"xyz", No:"0210001"}}, manager:"guowuyuan"}
	//main.Municipality2 main.Municipality2{City:main.City{Name:"Shanghai", No:"021", Age:0, GovernmentAddr:main.Addr{Region:"huangpu", street:"abc", No:"0210001"}}, Name:"moudu", manager:"guowuyuan"}
	//moudu Shanghai
}

new 初始化结构体

package main

import "fmt"

// 声明结构体
type Addr struct {
	Region string
	street string
	No     string
}

// 地级城市
type City struct {
	Name           string
	No             string
	Age            int
	GovernmentAddr Addr
}

func ExampleStructValue() {
	c1 := new(City)
	fmt.Printf("%T %#v\n", c1, c1)

	// Output:
	//*main.City &main.City{Name:"", No:"", Age:0, GovernmentAddr:main.Addr{Region:"", street:"", No:""}}
}

命名嵌入指针

package main

import "fmt"

// 声明结构体
type Addr struct {
	Region string
	street string
	No     string
}

// 地级城市
type City struct {
	Name           string
	No             string
	Age            int
	GovernmentAddr *Addr
}

// 直辖市,匿名结构体
type Municipality struct {
	*City

	manager string
}

func ExamplePointStruct() {
	var mc Municipality
	fmt.Printf("%T %#v\n", mc, mc)

	// 命名嵌入示例
	// 初始化
	addr := Addr{
		Region: "huangpu",
		street: "abc",
		No:     "0210001",
	}
	shanghai := City{
		Name:           "Shanghai",
		No:             "021",
		GovernmentAddr: &addr,
	}

	mc = Municipality{
		City:    &shanghai,
		manager: "guowuyuan",
	}
	fmt.Printf("%T %#v\n", mc, mc)

	// 访问属性,City 可以省略
	fmt.Println(mc.Name, mc.City.Name, mc.GovernmentAddr.street)

	// 修改属性
	mc.GovernmentAddr.street = "xyz"
	fmt.Printf("%T %#v\n", mc.GovernmentAddr, mc.GovernmentAddr)

	// Output:
	//main.Municipality main.Municipality{City:(*main.City)(nil), manager:""}
	//main.Municipality main.Municipality{City:(*main.City)(0xc00023d8c0), manager:"guowuyuan"}
	//Shanghai Shanghai abc
	//*main.Addr &main.Addr{Region:"huangpu", street:"xyz", No:"0210001"}
}

匿名嵌入指针

package main

import "fmt"

// 声明结构体
type Addr struct {
	Region string
	street string
	No     string
}

// 地级城市
type City struct {
	Name           string
	No             string
	Age            int
	GovernmentAddr *Addr
}

type Municipality2 struct {
	*City

	Name    string
	manager string
}

func ExampleAnonymityPointStruct() {
	// 匿名嵌入示例
	// 初始化
	var mc Municipality2
	fmt.Printf("%T %#v\n", mc, mc)

	addr := Addr{
		Region: "huangpu",
		street: "abc",
		No:     "0210001",
	}
	shanghai := City{
		Name:           "Shanghai",
		No:             "021",
		GovernmentAddr: &addr,
	}

	// 两个 Name 的不同
	mc2 := Municipality2{
		City:    &shanghai,
		Name:    "moudu",
		manager: "guowuyuan",
	}
	fmt.Printf("%T %#v\n", mc2, mc2)
	fmt.Println(mc2.Name, mc2.City.Name)

	// Output:
	//main.Municipality2 main.Municipality2{City:(*main.City)(nil), Name:"", manager:""}
	//main.Municipality2 main.Municipality2{City:(*main.City)(0xc0001a98c0), Name:"moudu", manager:"guowuyuan"}
	//moudu Shanghai
}

结构体是值类型验证

值类型和引用类型命名嵌入的区别

package main

import "fmt"

// 声明结构体
type Addr struct {
	Region string
	street string
	No     string
}

// 地级城市
type City struct {
	Name           string
	No             string
	Age            int
	GovernmentAddr Addr
}

type City2 struct {
	Name           string
	No             string
	Age            int
	GovernmentAddr *Addr
}

func changeAddr(c *City) {
	c.GovernmentAddr.street = "huoxing"
}

func ExampleStructValue() {
	// 结构体是值类型验证
	addr := Addr{
		Region: "huangpu",
		street: "abc",
		No:     "0210001",
	}
	c1 := City{
		Name:           "Shanghai",
		No:             "021",
		GovernmentAddr: addr,
	}

	c2 := c1
	c2.Name = "muodu"
	c2.GovernmentAddr.No = "021021"

	fmt.Printf("%T %#v %T %#v\n", c1, c1, c1.GovernmentAddr, c1.GovernmentAddr)
	fmt.Printf("%T %#v %T %#v\n", c2, c2, c2.GovernmentAddr, c2.GovernmentAddr)

	// 通过指针修改地址
	changeAddr(&c2)
	fmt.Printf("%T %#v %T %#v\n", c2, c2, c2.GovernmentAddr, c2.GovernmentAddr)

	// 指针类型的addr
	c3 := City2{
		Name:           "Shanghai",
		No:             "021",
		GovernmentAddr: &addr,
	}
	c4 := c3
	c4.GovernmentAddr.street = "update addr" // c3 c4 同时修改
	fmt.Printf("%T %#v %T %#v\n", c3, c3, c3.GovernmentAddr, c3.GovernmentAddr)
	fmt.Printf("%T %#v %T %#v\n", c4, c4, c4.GovernmentAddr, c4.GovernmentAddr)

	// Output:
	//main.City main.City{Name:"Shanghai", No:"021", Age:0, GovernmentAddr:main.Addr{Region:"huangpu", street:"abc", No:"0210001"}} main.Addr main.Addr{Region:"huangpu", street:"abc", No:"0210001"}
	//main.City main.City{Name:"muodu", No:"021", Age:0, GovernmentAddr:main.Addr{Region:"huangpu", street:"abc", No:"021021"}} main.Addr main.Addr{Region:"huangpu", street:"abc", No:"021021"}
	//main.City main.City{Name:"muodu", No:"021", Age:0, GovernmentAddr:main.Addr{Region:"huangpu", street:"huoxing", No:"021021"}} main.Addr main.Addr{Region:"huangpu", street:"huoxing", No:"021021"}
	//main.City2 main.City2{Name:"Shanghai", No:"021", Age:0, GovernmentAddr:(*main.Addr)(0xc00023d890)} *main.Addr &main.Addr{Region:"huangpu", street:"update addr", No:"0210001"}
	//main.City2 main.City2{Name:"Shanghai", No:"021", Age:0, GovernmentAddr:(*main.Addr)(0xc00023d890)} *main.Addr &main.Addr{Region:"huangpu", street:"update addr", No:"0210001"}
}

其他

结构体一般提供一个 New<struct>(...) 的方法

结构体方法

结构体方法格式:

func (变量 结构体或结构体指针) 函数名(形参列表) 返回类型列表 {
  函数体
}

说明

  • 结构体方法为特定类型定义的、只能由该类型调用的函数
  • 结构体方法只有该结构体或父结构体调用
  • (变量 结构体或结构体指针) 称为 接收者
  • 结构体方法 是添加了接收者的函数,接收者分为两种:值接受者指针接受者
    • 当使用结构体指针对象调用值接收者的方法时,Go编译器会自动将指针对象 解引用 为值引用方法
    • 当使用结构体值对象调用指针接收者者方法时,Go编译器会自动将值对象 取引用 为指针引用方法
    • 解引用取引用 发生在接收者中,对函数/方法的参数保持类型和数量意义对应
    • 当方法中需要修改属性值时,必须使用指针接收者。需注意指针接收者不能为 nil
  • 匿名嵌入
    • 若结构体匿名嵌入带有方法的结构体时,则在外部结构体可以调用嵌入结构体的方法,并且在调用时只有嵌入结构体的字段会传递给嵌入结构体方法的接收者
    • 若外部结构体和嵌入结构体有同名的方法时,则需要使用 对象.方法名 调用外部结构体方法,使用 对象.嵌入结构体名称.方法名 调用嵌入结构体方法
  • 什么是方法表达式?结构体上的方法
  • 方法可以赋值给变量,存储在数组、切片、map中,也可以作为参数传递给函数、或作为函数的返回值
  • 方法表达式赋值时,若方法接收者为值类型,则赋值时会将值类型复制一份;若方法接收者为指针,则自动解引用地址

基本使用示例

package main

import "fmt"

// 地级城市
type City struct {
	Name string
	No   string
}

func (c City) introduce() {
	fmt.Println("My Name is", c.Name, "no is", c.No)
}

func (c City) UpdateNo(no string) {
	c.No = no
}

func (c *City) introduce2() {
	fmt.Println("My Name is", c.Name, "no is", c.No)
}

func (c *City) UpdateNo2(no string) {
	c.No = no
}

func ExampleStructMethod() {
	//var c1 City // 该方法定义的是空指针,不能直接使用
	c1 := City{Name: "shanghai", No: "021"}
	c1.introduce()

	c1.Name = "muodu"
	c1.introduce()

	// 由于是值传递,下面这种修改属性的方法不生效
	c1.UpdateNo("0021")
	c1.introduce()

	// 只有使用指针时,在方法级别修改属性,调用处同步修改
	c1.UpdateNo2("0021") // 等同于 (&c1).UpdateNo("0021"),Golang 自动取引用,语法糖
	c1.introduce2()

	c2 := &City{Name: "shanghai", No: "021"}
	c2.UpdateNo2("0021")
	c2.introduce() // 自动解引用,语法糖

	// Output:
	//My Name is shanghai no is 021
	//My Name is muodu no is 021
	//My Name is muodu no is 021
	//My Name is muodu no is 0021
	//My Name is shanghai no is 0021
}

命名嵌套与匿名嵌套示例

  • String() 为内置函数,在print时使用
package main

import (
	"fmt"
	"strings"
)

type Addr struct {
	Name   string
	Region string
	street string
	No     string
}

// String() 为结构体的内置方法,用来输出字符串,仅适用于非值的类型,如果不存在 String() 方法,默认输出
func (a Addr) String() string {
	return strings.Join([]string{a.Region, a.street, a.No}, "-")
}

func (a Addr) GetStreet() string {
	return a.street
}

func (a Addr) GetName() string {
	return a.Name
}

// 命名嵌套结构体
type City struct {
	Name           string
	street         string
	GovernmentAddr Addr
}

func (c City) String() string {
	return strings.Join([]string{c.Name, fmt.Sprintf("%s", c.GovernmentAddr)}, ":")
}

// 匿名嵌套结构体
type City2 struct {
	Name string
	Addr
}

func (c City2) GetName() string {
	return c.Name
}

//func (c City2) String() string {
//	return strings.Join([]string{c.Name, fmt.Sprintf("%s", c.GovernmentAddr)}, ":")
//}

func ExampleStructMethod() {
	// 命名嵌套
	addr := Addr{
		Region: "huangpu",
		street: "abc",
		No:     "0210001",
	}

	shanghai := City{
		Name:           "Shanghai",
		GovernmentAddr: addr,
	}

	fmt.Println(shanghai)
	fmt.Println(shanghai.GovernmentAddr)

	// 匿名嵌套
	c2 := City2{
		Name: "shanghai",
		Addr: Addr{
			Name:   "addr1",
			Region: "huangpu",
			street: "abc",
			No:     "021001",
		},
	}
	// 省略匿名嵌套结构体名称
	fmt.Println(c2.GetStreet(), c2.Addr.GetStreet())
	fmt.Println(c2.GetName(), c2.Addr.GetName())

	// Output:
	//Shanghai:huangpu-abc-0210001
	//huangpu-abc-0210001
	//abc abc
	//shanghai addr1
}

方法值

package main

import (
	"fmt"
	"strings"
)

type Addr struct {
	Name   string
	Region string
	street string
	No     string
}

// String() 为结构体的内置方法,用来输出字符串,仅适用于非值的类型,如果不存在 String() 方法,默认输出
func (a Addr) String() string {
	return strings.Join([]string{a.Region, a.street, a.No}, "-")
}

func (a Addr) GetStreet() string {
	return a.street
}

func (a Addr) GetName() string {
	return a.Name
}

// 命名嵌套结构体
type City struct {
	name string
	Addr
}

// 值接受者
func (c City) String() string {
	return strings.Join([]string{c.name, c.Addr.String()}, ":")
}

// 指针接受者
func (c *City) Rename(name string) {
	c.name = name
}

func (c *City) GetName() string {
	return c.name
}

func ExampleStructMethod() {
	// 值接受者会对调用对象进行拷贝,因此不会在变化
	shanghai := City{
		name: "Shanghai",
		Addr: Addr{
			Region: "huangpu",
			street: "abc",
			No:     "0210001",
		},
	}

	ss := shanghai.String // 会将 shanghai 的值复制到 s 中,后续 s 的值不会在变化
	fmt.Printf("%T\n", ss)
	fmt.Println(ss())

	(&shanghai).Rename("muodu")
	// 获取的是旧值
	fmt.Println(ss())
	// 获取的是新值
	fmt.Println(shanghai)

	// 引用值接受者方法
	s2 := &City{
		name: "Shanghai",
		Addr: Addr{
			Region: "huangpu",
			street: "abc",
			No:     "0210001",
		},
	}
	sp := s2.String // s2 会自动解引用为值,然后复制一份,后续也不会改变了
	fmt.Printf("%T\n", sp)
	fmt.Println(sp())

	s2.Rename("muodu")
	fmt.Println(sp())
	fmt.Println(shanghai)

	// 引用指针接受者方法
	s3 := &City{
		name: "Shanghai",
		Addr: Addr{
			Region: "huangpu",
			street: "abc",
			No:     "0210001",
		},
	}

	sp2 := s3.GetName // 自动取引用,复制的是指针(地址),会随后续变化
	fmt.Printf("%T\n", sp2)
	fmt.Println(sp2())

	s3.Rename("muodu")
	fmt.Println(sp2())

	// Output:
	//func() string
	//Shanghai:huangpu-abc-0210001
	//Shanghai:huangpu-abc-0210001
	//muodu:huangpu-abc-0210001
	//func() string
	//Shanghai:huangpu-abc-0210001
	//Shanghai:huangpu-abc-0210001
	//muodu:huangpu-abc-0210001
	//func() string
	//Shanghai
	//muodu
}

方法表达式

方法表达式在赋值时

  • 若接受者为值类型的方法,可以使用类型名或类型指针名访问(go自动为指针变量生成隐式的指针类型的接受者方法)
  • 若接受者为指针类型的方法,只能使用指针名访问
package main

import (
	"fmt"
	"strings"
)

type Addr struct {
	Name   string
	Region string
	street string
	No     string
}

// String() 为结构体的内置方法,用来输出字符串,仅适用于非值的类型,如果不存在 String() 方法,默认输出
func (a Addr) String() string {
	return strings.Join([]string{a.Region, a.street, a.No}, "-")
}

func (a Addr) GetStreet() string {
	return a.street
}

func (a Addr) GetName() string {
	return a.Name
}

// 命名嵌套结构体
type City struct {
	name string
	Addr
}

// 值接受者,Golang 编译器会自动生成对应的指针接受者方法
func (c City) String() string {
	return strings.Join([]string{c.name, c.Addr.String()}, ":")
}

// 自动生成如下方法
//func (c *City) String() string {
//	return (*c).String() // 若String方法需修改内容,传递修值时不会对原数据造成影响
//}

// 指针接受者
func (c *City) SetName(name string) {
	c.name = name
}

func (c *City) GetName() string {
	return c.name
}

func ExampleStructMethod() {
	// 值接受者会对调用对象进行拷贝,因此不会在变化
	shanghai := City{
		name: "Shanghai",
		Addr: Addr{
			Region: "huangpu",
			street: "abc",
			No:     "0210001",
		},
	}

	str := City.String // 方法表达式,可以声明诶 str2 := *City.String,自动生成对应的指针接受者方法
	setName2 := (*City).SetName
	fmt.Printf("%T %T\n", str, setName2)

	// 方法表达式调用,必须类型匹配
	fmt.Println(str(shanghai))

	//
	setName2(&shanghai, "Muodu")
	fmt.Println(str(shanghai))

	// Output:
	//func(main.City) string func(*main.City, string)
	//Shanghai:huangpu-abc-0210001
	//Muodu:huangpu-abc-0210001
}

结构体数据排序

package main

import (
	"fmt"
	"sort"
)

func ExampleSort() {
	// https://pkg.go.dev/sort#Slice
	// 使用数组的第二个元素比较大小排序
	list := [][2]int{{1, 3}, {5, 9}, {4, 5}, {6, 2}, {5, 8}}
	sort.Slice(list, func(i, j int) bool {
		return list[i][1] < list[j][1]
	})
	fmt.Println(list)

	// sort struct
	people := []struct {
		Name string
		Age  int
	}{
		{"Gopher", 7},
		{"Alice", 55},
		{"Vera", 24},
		{"Bob", 75},
	}
	sort.Slice(people, func(i, j int) bool {
		return people[i].Name < people[j].Name
	})
	fmt.Println("By name:", people)

	sort.Slice(people, func(i, j int) bool { return people[i].Age < people[j].Age })
	fmt.Println("By age:", people)

	// Output:
	//[[6 2] [1 3] [4 5] [5 8] [5 9]]
	//By name: [{Alice 55} {Bob 75} {Gopher 7} {Vera 24}]
	//By age: [{Gopher 7} {Vera 24} {Alice 55} {Bob 75}]
}
Home Archives Categories Tags Statistics
本文总阅读量 次 本站总访问量 次 本站总访客数