一文教你如何快速学会Go的struct数据类型

目录
  • 什么是结构体
  • 创建结构体
  • 创建匿名结构体
  • 访问结构体字段
  • 结构体零值
  • 结构体指针
  • 匿名字段
  • 结构体嵌套
  • 字段升级
  • 结构体导出
  • 结构体比较

什么是结构体

结构是表示字段集合的用户定义类型。它可以用于将数据分组为单个单元而不是将每个数据作为单独的值的地方。

例如,员工有firstName、lastName和age。将这三个属性分组到一个名为Employee

type Employee struct {
    firstName string
    lastName  string
    age       int
}

上面的代码段声明了一个结构类型Employee,其中包含字段firstName、lastName和age。上面的Employee结构称为命名结构,因为它创建了一个名为Employme的新数据类型,可以使用该数据类型创建Employ结构。

通过在一行中声明属于同一类型的字段,然后在类型名称后面加上该字段,也可以使该结构更加紧凑。在上面的struct中,firstName和lastName属于同一类型字符串,因此该struct可以重写为:

type Employee struct {
    firstName, lastName string
    age                 int
}

尽管上面的语法节省了几行代码,但它并没有使字段声明显式。请避免使用上述语法。

创建结构体

让我们使用以下简单程序声明一个命名的structEmployee。

package main

import (
    "fmt"
)

type Employee struct {
    firstName string
    lastName  string
    age       int
    salary    int
}

func main() {

    //creating struct specifying field names
    emp1 := Employee{
        firstName: "Sam",
        age:       25,
        salary:    500,
        lastName:  "Anderson",
    }

    //creating struct without specifying field names
    emp2 := Employee{"Thomas", "Paul", 29, 800}

    fmt.Println("Employee 1", emp1)
    fmt.Println("Employee 2", emp2)
}

在上述程序的第7行中,我们创建了一个命名的结构类型Employee。在上述程序的第17行中,emp1结构是通过为每个字段名指定值来定义的。声明结构类型时,字段的顺序不必与字段名的顺序相同。在这种情况下。我们已更改lastName的位置并将其移到末尾。这将不会有任何问题。

在上述程序的第25行中,通过省略字段名来定义emp2。在这种情况下,必须保持字段的顺序与结构声明中指定的顺序相同。请避免使用此语法,因为它会使您难以确定哪个字段的值。我们在此处指定此格式只是为了理解这也是一个有效语法:)

以上程序打印为:

Employee 1 {Sam Anderson 25 500}  
Employee 2 {Thomas Paul 29 800}

创建匿名结构体

可以在不创建新数据类型的情况下声明结构。这些类型的结构称为匿名结构。

package main

import (
    "fmt"
)

func main() {
    emp3 := struct {
        firstName string
        lastName  string
        age       int
        salary    int
    }{
        firstName: "Andreah",
        lastName:  "Nikola",
        age:       31,
        salary:    5000,
    }

    fmt.Println("Employee 3", emp3)
}

在上述程序的第8行中,定义了一个匿名结构变量emp3。正如我们已经提到的,这个结构称为anonymous,因为它只创建一个新的结构变量emp3,而没有定义任何新的结构类型,如命名结构。

上述代码打印的结果为:

Employee 3 {Andreah Nikola 31 5000}

访问结构体字段

运算符.用于访问结构的各个字段。

package main

import (
    "fmt"
)

type Employee struct {
    firstName string
    lastName  string
    age       int
    salary    int
}

func main() {
    emp6 := Employee{
        firstName: "Sam",
        lastName:  "Anderson",
        age:       55,
        salary:    6000,
    }
    fmt.Println("First Name:", emp6.firstName)
    fmt.Println("Last Name:", emp6.lastName)
    fmt.Println("Age:", emp6.age)
    fmt.Printf("Salary: $%d\n", emp6.salary)
    emp6.salary = 6500
    fmt.Printf("New Salary: $%d", emp6.salary)
}

上面程序中的emp6.firstName访问emp6结构的firstName字段。在第25行中,我们修改了员工的工资。此程序打印。

First Name: Sam  
Last Name: Anderson  
Age: 55  
Salary: $6000  
New Salary: $6500

结构体零值

当定义了一个结构并且没有用任何值显式初始化它时,默认情况下会为该结构的字段分配零值。

package main

import (
    "fmt"
)

type Employee struct {
    firstName string
    lastName  string
    age       int
    salary    int
}

func main() {
    var emp4 Employee //zero valued struct
    fmt.Println("First Name:", emp4.firstName)
    fmt.Println("Last Name:", emp4.lastName)
    fmt.Println("Age:", emp4.age)
    fmt.Println("Salary:", emp4.salary)
}

上面的程序定义了emp4,但没有用任何值初始化。因此,firstName和lastName被指定为字符串的零值,字符串为空字符串“”,age和salary被指定为零值int,即0。此程序打印

First Name:  
Last Name:  
Age: 0  
Salary: 0

也可以为某些字段指定值,而忽略其余字段。在这种情况下,被忽略的字段被赋值为零。

package main

import (
    "fmt"
)

type Employee struct {
    firstName string
    lastName  string
    age       int
    salary    int
}

func main() {
    emp5 := Employee{
        firstName: "John",
        lastName:  "Paul",
    }
    fmt.Println("First Name:", emp5.firstName)
    fmt.Println("Last Name:", emp5.lastName)
    fmt.Println("Age:", emp5.age)
    fmt.Println("Salary:", emp5.salary)
}

在上面的程序中。第16号和第17号,firstName和lastName被初始化,而年龄和薪水没有初始化。因此,年龄和工资被指定为零值。此程序输出:

First Name: John  
Last Name: Paul  
Age: 0  
Salary: 0

结构体指针

也可以创建指向结构的指针。

package main

import (
    "fmt"
)

type Employee struct {
    firstName string
    lastName  string
    age       int
    salary    int
}

func main() {
    emp8 := &Employee{
        firstName: "Sam",
        lastName:  "Anderson",
        age:       55,
        salary:    6000,
    }
    fmt.Println("First Name:", (*emp8).firstName)
    fmt.Println("Age:", (*emp8).age)
}

上面程序中的emp8是指向Employee结构的指针。(*emp8)。firstName是访问emp8结构的firstName字段的语法。此程序打印:

First Name: Sam  
Age: 55

Go语言为我们提供了使用emp8.firstName而不是显式取消引用(*emp8)的选项。firstName以访问firstName字段。

package main

import (
    "fmt"
)

type Employee struct {
    firstName string
    lastName  string
    age       int
    salary    int
}

func main() {
    emp8 := &Employee{
        firstName: "Sam",
        lastName:  "Anderson",
        age:       55,
        salary:    6000,
    }
    fmt.Println("First Name:", emp8.firstName)
    fmt.Println("Age:", emp8.age)
}

我们已经使用emp8.firstName访问上述程序中的firstName字段,该程序还输出:

First Name: Sam  
Age: 55

匿名字段

可以使用只包含类型而不包含字段名的字段创建结构。这类字段称为匿名字段。下面的代码段创建了一个struct Person,它有两个匿名字段string和int:

type Person struct {
    string
    int
}

即使匿名字段没有显式名称,默认情况下,匿名字段的名称是其类型的名称。例如,在上面的Person结构中,虽然字段是匿名的,但默认情况下它们采用字段类型的名称。所以Person结构有两个字段,分别是名称字符串和int。

package main

import (
    "fmt"
)

type Person struct {
    string
    int
}

func main() {
    p1 := Person{
        string: "naveen",
        int:    50,
    }
    fmt.Println(p1.string)
    fmt.Println(p1.int)
}

在上述程序的第17行和第18行中,我们访问Person结构的匿名字段,使用它们的类型作为字段名,分别是string和int。上述程序的输出为:

naveen  
50

结构体嵌套

结构可能包含字段,而字段又是结构。这些类型的结构称为嵌套结构。

package main

import (
    "fmt"
)

type Address struct {
    city  string
    state string
}

type Person struct {
    name    string
    age     int
    address Address
}

func main() {
    p := Person{
        name: "Naveen",
        age:  50,
        address: Address{
            city:  "Chicago",
            state: "Illinois",
        },
    }

    fmt.Println("Name:", p.name)
    fmt.Println("Age:", p.age)
    fmt.Println("City:", p.address.city)
    fmt.Println("State:", p.address.state)
}

上述程序中的Person结构具有字段地址,而字段地址又是一个结构。此程序打印:

Name: Naveen  
Age: 50  
City: Chicago  
State: Illinois

字段升级

属于结构中匿名结构字段的字段称为提升字段,因为可以像访问包含匿名结构字段结构一样访问它们。我可以理解这个定义相当复杂,所以让我们深入研究一些代码来理解它。

type Address struct {
    city string
    state string
}
type Person struct {
    name string
    age  int
    Address
}

在上面的代码段中,Person结构有一个匿名字段Address,它是一个结构。现在,Address的字段,即city和state,被称为promoted字段,因为可以像直接在Person结构本身中声明一样访问它们。

package main

import (
    "fmt"
)

type Address struct {
    city  string
    state string
}
type Person struct {
    name string
    age  int
    Address
}

func main() {
    p := Person{
        name: "Naveen",
        age:  50,
        Address: Address{
            city:  "Chicago",
            state: "Illinois",
        },
    }

    fmt.Println("Name:", p.name)
    fmt.Println("Age:", p.age)
    fmt.Println("City:", p.city)   //city is promoted field
    fmt.Println("State:", p.state) //state is promoted field
}

在上面程序的第29行和第30行中,可以访问提升字段city和state,就好像它们是使用语法p.city和p.state在结构p中声明的一样。此程序打印:

Name: Naveen  
Age: 50  
City: Chicago  
State: Illinois

结构体导出

如果结构类型以大写字母开头,则它是导出类型,可以从其他包访问。类似地,如果结构的字段以caps开头,则可以从其他包访问它们。让我们编写一个具有自定义包的程序来更好地理解这一点。在Documents目录中创建名为structs的文件夹。请随意在任何您喜欢的地方创建它。我更喜欢我的文档目录。

mkdir ~/Documents/structs

创建一个gomod,并命名为structs

cd ~/Documents/structs/  
go mod init structs

创建另外一个目录computer申明一个结构体。

mkdir computer

创建一个spec.go文件,并写入如下内容:

package computer

type Spec struct { //exported struct
    Maker string //exported field
    Price int //exported field
    model string //unexported field

}

上面的代码片段创建了一个程序包计算机,其中包含一个导出的结构类型Spec,其中有两个导出的字段Maker和Price,以及一个未导出的字段模型。让我们从主包导入这个包并使用Spec结构。

创建名为main的文件。进入structs目录并在main.go中编写以下程序:

package main

import (
    "structs/computer"
    "fmt"
)

func main() {
    spec := computer.Spec {
            Maker: "apple",
            Price: 50000,
        }
    fmt.Println("Maker:", spec.Maker)
    fmt.Println("Price:", spec.Price)
}

这个结构体如下结构体:

├── structs
│   ├── computer
│   │   └── spec.go
│   ├── go.mod
│   └── main.go

在上面程序的第4行,我们导入计算机包。在第13行和第14行,我们访问struct Spec的两个导出字段Maker和Price。这个程序可以通过执行命令go-install,然后执行structs命令来运行。

go install  
structs

运行之后如下结果:

Maker: apple  
Price: 50000

如果我们试图访问未报告的字段模型,编译器会抱怨。更换main的内容。使用以下代码。

package main

import (
    "structs/computer"
    "fmt"
)

func main() {
    spec := computer.Spec {
            Maker: "apple",
            Price: 50000,
            model: "Mac Mini",
        }
    fmt.Println("Maker:", spec.Maker)
    fmt.Println("Price:", spec.Price)
}

在上述程序的第12行中,我们尝试访问未报告的字段模型。运行此程序将导致编译错误。

# structs
./main.go:12:13: unknown field 'model' in struct literal of type computer.Spec

由于模型字段未报告,因此无法从其他包访问它。

结构体比较

结构是值类型,如果它们的每个字段都是可比较的,则可以进行比较。如果两个结构变量的对应字段相等,则认为它们相等。

package main

import (
    "fmt"
)

type name struct {
    firstName string
    lastName  string
}

func main() {
    name1 := name{
        firstName: "Steve",
        lastName:  "Jobs",
    }
    name2 := name{
        firstName: "Steve",
        lastName:  "Jobs",
    }
    if name1 == name2 {
        fmt.Println("name1 and name2 are equal")
    } else {
        fmt.Println("name1 and name2 are not equal")
    }

    name3 := name{
        firstName: "Steve",
        lastName:  "Jobs",
    }
    name4 := name{
        firstName: "Steve",
    }

    if name3 == name4 {
        fmt.Println("name3 and name4 are equal")
    } else {
        fmt.Println("name3 and name4 are not equal")
    }
}

在上面的程序中,名称结构类型包含两个字符串字段。由于字符串是可比较的,因此可以比较类型名的两个结构变量。在上面的程序中,name1和name2相等,而name3和name4不相等。此程序将输出:

name1 and name2 are equal  
name3 and name4 are not equal

如果结构变量包含不可比较的字段,那么它们就不可比较(感谢reddit的alasija指出这一点)。

package main

import (
    "fmt"
)

type image struct {
    data map[int]int
}

func main() {
    image1 := image{
        data: map[int]int{
            0: 155,
        }}
    image2 := image{
        data: map[int]int{
            0: 155,
        }}
    if image1 == image2 {
        fmt.Println("image1 and image2 are equal")
    }
}

在上面的程序中,图像结构类型包含类型映射的字段数据。地图是不可比较的,因此无法比较image1和image2。如果运行此程序,编译将失败并返回错误。

./prog.go:20:12: invalid operation: image1 == image2 (struct containing map[int]int cannot be compared)

到此这篇关于一文教你如何快速学会Go的struct数据类型的文章就介绍到这了,更多相关Go struct数据类型内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • Go语言中的Struct结构体

    一.Struct结构体 Go语言中没有像C#.Java一样的Class,只有Struct这样的结构体.Go语言使用type关键字来定义一个类型. 如下: type User struct { Name string Age int32 Sex int16 AvatarUrl string } 二.Struct的声明以及初始化 初始化方法一:直接定义,通过变量名.成员名的方式赋值 //初始化方法1 var user1 User user1.Name = "BigOrange" user1

  • golang中结构体嵌套接口的实现

    在golang中结构体A嵌套另一个结构体B见的很多,可以扩展A的能力. A不仅拥有了B的属性,还拥有了B的方法,这里面还有一个字段提升的概念. 示例: package main import "fmt" type Worker struct {     Name string     Age int     Salary } func (w Worker) fun1() {     fmt.Println("Worker fun1") } type Salary s

  • Golang实现不被复制的结构体的方法

    目录 不允许复制的结构体 实现原理 结论 不允许复制的结构体 sync包中的许多结构都是不允许拷贝的,比如sync.Cond,sync.WaitGroup,sync.Pool, 以及sync包中的各种锁,因为它们自身存储了一些状态(比如等待者的数量),如果你尝试复制这些结构体: var wg1 sync.WaitGroup wg2 := wg1 // 将 wg1 复制一份,命名为 wg2 // ... 那么你将在你的 IDE 中看到一个醒目的警告: assignment copies lock

  • Go Struct结构体的具体实现

    目录 什么是结构体 1. 基本实例化(方法1) 2. new实例化(方法2) 3. 键值对初始化(方法3 结构体能够使用指针就使用指针) 结构体方法和接收者 encoding-json包 1. struct与json 2. struct tag 什么是结构体 Go语言中没有“类”的概念,也不支持“类”的继承等面向对象的概念.(继承,多态,封装) Go语言中通过结构体的内嵌再配合接口比面向对象具有更高的扩展性和灵活性. 1. 基本实例化(方法1) 只有当结构体实例化时,才会真正地分配内存(其实也就

  • Go语言中Struct与继承与匿名字段和内嵌结构体全面详解

    目录 定义 内嵌结构体 在golang中,采用匿名结构体字段来模拟继承关系.这个时候,可以说 Student 是继承自 Person . type Person struct { name string age int sex string } func (Person) SayHello(){ fmt.Println("this is from Person") } type Student struct { Person school string } func main() {

  • Go语言类型内嵌和结构体内嵌的具体使用

    目录 内嵌结构体 结构内嵌特性 结构体可以包含一个或多个匿名(或内嵌)字段,即这些字段没有显式的名字,只有字段的类型是必须的,此时类型也就是字段的名字.匿名字段本身可以是一个结构体类型,即结构体可以包含内嵌结构体. 可以粗略地将这个和面向对象语言中的继承概念相比较,随后将会看到它被用来模拟类似继承的行为.Go语言中的继承是通过内嵌或组合来实现的,所以可以说,在Go语言中,相比较于继承,组合更受青睐. 考虑如下的程序: package main import "fmt" type inn

  • 一文教你如何快速学会Go的struct数据类型

    目录 什么是结构体 创建结构体 创建匿名结构体 访问结构体字段 结构体零值 结构体指针 匿名字段 结构体嵌套 字段升级 结构体导出 结构体比较 什么是结构体 结构是表示字段集合的用户定义类型.它可以用于将数据分组为单个单元而不是将每个数据作为单独的值的地方. 例如,员工有firstName.lastName和age.将这三个属性分组到一个名为Employee. type Employee struct { firstName string lastName string age int } 上面

  • 一文教你如何快速学会Go的切片和数组数据类型

    目录 什么是数组 声明数组 数组是值类型 数组的长度 迭代数组 多维数组 切片 创建切片 修改切片 切片长度和容量 使用make创建切片 切片追加 切片作为函数参数 多维切片 什么是数组 数组是属于同一类型的元素的集合.例如,整数 5.8.9.79.76 的集合形成一个数组.Go 中不允许混合不同类型的值,例如,同时包含字符串和整数的数组. 声明数组 数组属于类型 . 表示数组中的元素数,并表示每个元素的类型.元素的数量也是类型的一部分(我们稍后将对此进行更详细的讨论.[n]TnTn 有不同的方

  • 快速学会Dubbo的配置环境及相关配置

    目录 1. Dubbo相关概念 1.1 自动服务发现工作原理 2. 启动文件 2.1 zookeeper-3.4.11\bin\zkServer.cmd 2.2 zookeeper-3.4.11\bin\zkCli.cmd 2.3 java -jar dubbo-admin-0.0.1-SNAPSHOT.jar 2.4 dubbo-monitor-simple-2.0.0\assembly.bin\start.bat 3. 修改项目文件 3.1 在pom.xml文件里导入相关依赖 3.2 在ap

  • 教你快速学会JPA中所有findBy语法规则

    目录 快速学会JPA中所有findBy语法规则 1.findBy findAllBy的区别 2.JPA中支持的关键词 JPA findBy 语法总结 1.JPA同时查询两个属性 2.表格汇总 3.Spring Data JPA框架在进行方法名解析时 4.JPA的NamedQueries 5.JPQL查询 快速学会JPA中所有findBy语法规则 1.findBy findAllBy的区别 它们之间没有区别,它们将执行完全相同的查询,当从方法名称派生查询时,Spring Data会忽略All部分.

  • 一文带你快速学会JDBC及获取连接的五种方式

    目录 1. JDBC基本介绍 2. JDBC快速入门 2.1 JDBC程序编写步骤 2.2 案例演示 2.2.1 前置工作,在数据库中建立对应表 2.2.2 前置工作,导入MySQL数据库的对应jar包 3. 相关类的介绍 3.1 Statement 3.2 ResultSet[结果集] 3.3 PreparedStatement 4. 关闭资源 5. 获取数据库连接的五种方式 方式一 方式二 方式三 方式四 方式五 总结 快速学会JDBC及获取连接的五种方式 1. JDBC基本介绍 JDBC为

  • 六分钟带你快速学会react中的useMemo

    目录 概念 useMemo 栗子 筛选偶数 每秒获取时间刷新页面 如何优化 useCallback.React.memo 总结 概念 react 中是通过一次次的 re-render (重新渲染)保持我们的值及时的更新到页面上的,每次重新渲染都是一次快照,可以把它想象成一张张的照片,在某个时刻它应该是什么样子的 useMemo 把创建函数和依赖数组项作为参数传入 useMemo,它仅仅会在数组依赖项中的值改变时才会重新计算值 这种优化有助于避免在每次渲染时都进行高开销的计算 useMemo 的函

  • 5分钟快速学会spring boot整合JdbcTemplate的方法

    一.前言 Spring Boot是由Pivotal团队提供的全新框架,其设计目的是用来简化新Spring应用的初始搭建以及开发过程.该框架使用了特定的方式来进行配置,从而使开发人员不再需要定义样板化的配置.通过这种方式,Spring Boot致力于在蓬勃发展的快速应用开发领域(rapid application development)成为领导者.本文主要介绍的是关于springboot整合JdbcTemplate. 在此记录下,分享给大家. 二.springboot整合JdbcTemplate

  • 3分钟快速学会在ASP.NET Core MVC中如何使用Cookie

    一.Cookie是什么? 我的朋友问我cookie是什么,用来干什么的,可是我居然无法清楚明白简短地向其阐述cookie,这不禁让我陷入了沉思:为什么我无法解释清楚,我对学习的方法产生了怀疑!所以我们在学习一个东西的时候,一定要做到知其然知其所以然. HTTP协议本身是无状态的.什么是无状态呢,即服务器无法判断用户身份.Cookie实际上是一小段的文本信息).客户端向服务器发起请求,如果服务器需要记录该用户状态,就使用response向客户端浏览器颁发一个Cookie.客户端浏览器会把Cooki

  • 三分钟带你快速学会微信小程序的条件渲染

    目录 前言 1.wx:if实现条件渲染 2.block结合wx:if使用 3.hiden实现条件渲染 4. wx:if vs hidden 补充:hidden 和 wx:if 的区别 总结 前言 这篇文章是三分钟学会小程序的条件渲染教程,所谓的条件渲染就是判断是否需要把代码渲染到展示页面上.使用以下三种方式可以实现条件渲染,各自有不同的应用场景,只有熟练掌握其用法,在使用时才能得心应手. 1.wx:if实现条件渲染 在框架中,使用 wx:if="" 来判断是否需要渲染该代码块: <

  • 5分钟快速学会spring boot整合Mybatis的方法

    一.前言 记得刚接触SpringBoot时,大吃一惊,世界上居然还有这么省事的框架,立马感叹:SpringBoot是世界上最好的框架.哈哈! 下面这篇文章主要给大家介绍了关于springboot整合Mybatis.在此记录下,分享给大家. 二.springboot整合Mybatis  ​ 1.pom文件 依赖引入 <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-b

随机推荐