关于go:你有对象类我有结构体Go-lang118入门精炼教程由白丁入鸿儒go-lang结构体struct的使用EP06

再续前文,在面向对象层面,Python做到了超神:万物皆为对象,而Ruby,则罗唆就是神:飞花摘叶皆可对象。二者都提供对象类操作以及继承的形式为面向对象张目,但Go lang显然有一些特立独行,因为它没有传统的类,也没有继承,取而代之的是构造和组合的形式,也就是构造体(struct)的形式来组织代码,达到相似类的成果。

构造体struct的申明

在 Go lang中应用上面的语法是对构造体的申明:

type struct_name struct {  
    attribute_name1   attribute_type  
    attribute_name2   attribute_type  
    ...  
}

假如定义一个名为 Lesson(课程) 的构造体:

type Lesson struct {  
    name   string //名称  
    target string //学习指标  
    spend  int    //学习破费工夫  
}

这里申明了一个构造体类型 Lesson ,它有 name 、 target 和 spend 三个属性,相当于Python中类的公有属性。

也能够把雷同类型的属性申明在同一行,这样能够使构造体变得更加紧凑:

type Lesson2 struct {  
    name, target    string  
    spend             int  
}

Lesson 称为命名的构造体(Named Structure) ,这里 Lesson 作为一种新的数据类型而存在,而它能够用于创立 Lesson 类型的构造体变量。

此外,申明构造体时也能够不必申明一个新类型,这样的构造体类型称为匿名构造体(Anonymous Structure) ,能够了解为构造体变量:

var MyLesson struct {  
    name, target    string  
    spend             int  
}

构造体struct的创立

申明了构造体之后,咱们能够依据申明好的构造体类型来创立构造体,这个过程有点像Python语言中类的实例化:

import "fmt"  
  
type Lesson struct {  
    name, target string  
    spend        int  
}  
  
func main() {  
    // 应用字段名创立构造体  
    lesson1 := Lesson{  
        name:   "go lang 1.18",  
        target: "学习Go lang,并实现web开发工作",  
        spend:  1,  
    }  
    // 不应用字段名创立构造体  
    lesson2 := Lesson{"go lang 1.18", "学习Go lang,并实现web开发工作", 1}  
  
    fmt.Println("lesson1 ", lesson1)  
    fmt.Println("lesson2 ", lesson2)  
}

程序返回:

lesson1  {go lang 1.18 学习Go lang,并实现web开发工作 1}  
lesson2  {go lang 1.18 学习Go lang,并实现web开发工作 1}

这里字段名能够做省略操作,但要留神传递程序。

此外,也能够创立匿名构造体:

package main  
  
import "fmt"  
  
func main() {  
    // 创立匿名构造体变量  
    mylesson := struct {  
        name, target string  
        spend        int  
    }{  
        name:   "Go lang 1.18",  
        target: "学习go lang,实现web需要",  
        spend:  1,  
    }  
  
    fmt.Println("mylesson ", mylesson)  
}

当定义好的构造体没有被显式赋值时,构造体的字段将会默认赋为相应类型的零值:

package main  
  
import "fmt"  
  
type Lesson struct {  
    name, target string  
    spend        int  
}  
  
func main() {  
    // 不初始化构造体  
    var lesson = Lesson{}  
  
    fmt.Println("lesson ", lesson)  
}

程序返回:

lesson  {  0}

假如某个或者某几个字段没有赋值,也会默认赋值为对应根本数据类型的零值:

package main  
  
import "fmt"  
  
type Lesson struct {  
    name, target    string  
    spend             int  
}  
  
func main() {  
    // 为构造体指定字段赋初值  
    var lesson = Lesson{  
        name: "go lang 1.18",  
    }  
  
    // 下面的构造体变量 lesson 只初始化了 name 字段, 其余字段没有初始化,所以会被初始化为零值  
    fmt.Println("lesson ", lesson)  
}

程序返回:

lesson  {go lang 1.18  0}

构造体struct的属性与指针

通过点操作符 . 能够拜访构造体的属性:

package main  
  
import "fmt"  
  
type Lesson struct {  
    name, target string  
    spend        int  
}  
  
func main() {  
  
    var lesson = Lesson{  
        name: "go lang 1.18",  
    }  
  
    fmt.Println("lesson name ", lesson.name)  
}

程序返回:

lesson name  go lang 1.18

也能够应用点操作符 . 对构造体的字段进行赋值操作:

package main  
  
import "fmt"  
  
type Lesson struct {  
    name, target string  
    spend        int  
}  
  
func main() {  
  
    var lesson = Lesson{  
        name: "go lang 1.18",  
    }  
  
    fmt.Println("lesson name ", lesson.name)  
  
    lesson.name = "Python 3.11"  
  
    fmt.Println("lesson name ", lesson.name)  
  
}

程序返回:

lesson name  go lang 1.18  
lesson name  Python 3.11

须要留神的是,赋值变量和构造体属性的根本数据类型要统一。

在前一篇:借问变量何处存,牧童笑称用指针,Go lang1.18入门精炼教程,由白丁入鸿儒,go lang类型指针(Pointer)的应用EP05咱们应用了指针来操作变量,指针也能够指向构造体:

package main  
  
import "fmt"  
  
type Lesson struct {  
    name, target string  
    spend        int  
}  
  
func main() {  
    lesson := &Lesson{"go lang 1.18", "实现对应web需要", 1}  
    fmt.Println("lesson name: ", (*lesson).name)  
    fmt.Println("lesson name: ", lesson.name)  
}

程序返回:

lesson name:  go lang 1.18  
lesson name:  go lang 1.18

lesson是一个指向构造体 Lesson 的指针,下面用 (*lesson).name 拜访 lesson的 name 字段,lesson.name 是代替 (*lesson).name 的解援用拜访。

在创立构造体时,属性能够只有类型没有属性名,这种属性称为匿名字段(Anonymous Field) :

package main  
  
import "fmt"  
  
type Lesson struct {  
    string  
    int  
}  
  
func main() {  
    lesson := Lesson{"go lang 1.18", 1}  
    fmt.Println("lesson ", lesson)  
    fmt.Println("lesson string: ", lesson.string)  
    fmt.Println("lesson int: ", lesson.int)  
}

程序返回:

lesson  {go lang 1.18 1}  
lesson string:  go lang 1.18  
lesson int:  1

这里程序结构体定义了两个匿名字段,尽管这两个字段没有字段名,但匿名字段的名称默认就是它的类型。所以下面的构造体 Lesoon 有两个名为 string 和 int 的字段,同样须要留神程序和字段数据类型的匹配问题。

嵌套构造体

构造体自身也反对复合的嵌套构造:

package main  
  
import "fmt"  
  
type Author struct {  
    name string  
}  
  
type Lesson struct {  
    name, target string  
    spend        int  
    author       Author  
}  
  
func main() {  
    lesson := Lesson{  
        name:  "go lang 1.18",  
        spend: 1,  
    }  
    lesson.author = Author{  
        name: "佚名",  
    }  
    fmt.Println("lesson name:", lesson.name)  
    fmt.Println("lesson spend:", lesson.spend)  
    fmt.Println("lesson author name:", lesson.author.name)  
}

程序返回:

lesson name: go lang 1.18  
lesson spend: 1  
lesson author name: 佚名

这里构造体Author自身作为构造体Lesson的一个属性而存在,赋值时,通过父构造体间接调用子结构体名称即可。

如果构造体中有匿名的构造体类型字段,则该匿名构造体里的字段就称为晋升字段(Promoted Fields) 。这是因为晋升字段就像是属于内部构造体一样,能够用内部构造体间接拜访:

package main  
  
import (  
    "fmt"  
)  
  
type Address struct {  
    city, state string  
}  
type Person struct {  
    name string  
    age  int  
    Address  
}  
  
func main() {  
    var p Person  
    p.name = "Naveen"  
    p.age = 50  
    p.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  
}

零碎返回:

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

如果咱们把 Person 构造体中的字段 address 间接用匿名字段 Address 代替, Address 构造体的字段例如 city 就不必像 p.address.city这样拜访,而是应用 p.address 就能拜访 Address 构造体中的 address字段。当初构造体 Address 有city字段,拜访字段就像在 Person 里间接申明的一样,因而咱们称之为晋升字段,说白了就是把子结构体的字段晋升为父构造体的字段,然而定义还是在子结构体之中。

假如构造体的全副属性都是能够比拟的,那么构造体也是能够比拟的,那样的话两个构造体将能够应用 == 或 != 运算符进行比拟。能够通过==运算符或 DeeplyEqual()函数比拟两个构造雷同的类型并蕴含雷同的字段值:

package main  
  
import (    
    "fmt"  
)  
  
type name struct {    
    firstName string  
    lastName string  
}  
  
  
func main() {    
    name1 := name{"Steve", "Jobs"}  
    name2 := name{"Steve", "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{}  
    name4.firstName = "Steve"  
    if name3 == name4 {  
        fmt.Println("name3 and name4 are equal")  
    } else {  
        fmt.Println("name3 and name4 are not equal")  
    }  
}

程序返回:

name1 and name2 are equal  
name3 and name4 are not equal

如果构造变量蕴含的字段是不可比拟的,那么构造变量是不可比拟的:

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")  
    }  
}

程序报错:

# command-line-arguments  
.\test.go:18:5: invalid operation: image1 == image2 (struct containing map[int]int cannot be compared)

由此可知,构造体的比拟能够了解为其属性的批量比拟。

构造体绑定办法

在 Go lang中无奈在构造体外部定义方法,这一点与 C 语言相似:

package main  
  
import "fmt"  
  
// Lesson 定义一个名为 Lesson 的构造体  
type Lesson struct {  
    name, target string  
    spend        int  
}  
  
// ShowLessonInfo 定义一个与 Lesson 的绑定的办法  
func (l Lesson) ShowLessonInfo() {  
    fmt.Println("name:", l.name)  
    fmt.Println("target:", l.target)  
}  
  
func main() {  
    l := Lesson{  
        name: "go lang 1.1 8",  
    }  
    l.ShowLessonInfo()  
}

程序返回:

name: go lang 1.1 8  
target:

这里定义了一个与构造体 Lesson 绑定的办法 ShowLessonInfo() ,其中 ShowLessonInfo 是办法名, (l Lesson) 示意将此办法与 Lesson 的实例绑定,这在 Go lang中称为接收者,而 l 示意实例自身,相当于 Python 中的 self ,在办法内能够应用实例自身.属性名称来拜访实例属性。

如果绑定构造体的办法中要扭转实例的属性时,必须应用指针作为办法的接收者:



package main  
  
import "fmt"  
  
// Lesson 定义一个名为 Lesson 的构造体  
type Lesson struct {  
    name, target string  
    spend        int  
}  
  
// ShowLessonInfo 定义一个与 Lesson 的绑定的办法  
func (l Lesson) ShowLessonInfo() {  
    fmt.Println("spend:", l.spend)  
}  
  
// AddTime 定义一个与 Lesson 的绑定的办法,使 spend 值加 n  
func (l *Lesson) AddTime(n int) {  
    l.spend = l.spend + n  
}  
  
func main() {  
    lesson13 := Lesson{  
        spend: 1,  
    }  
    fmt.Println("增加add办法前")  
    lesson13.ShowLessonInfo()  
    lesson13.AddTime(5)  
    fmt.Println("增加add办法后")  
    lesson13.ShowLessonInfo()  
}  


程序返回:

增加add办法前  
spend: 1  
增加add办法后  
spend: 6

结语

大抵上,Go lang的构造体就是对象类的变种,尽管并没有显性的继承操作,然而通过嵌套构造体和晋升字段两种形式,也能达到“继承”的成果,构造体的最终目标和成果与对象类并无二致,类比的话,有点像电脑散热的两种形式:风冷和水冷,咱们不能说哪一种形式更好或者不好,只有这种形式能够实现我的项目中的需要即可,不论黑猫白猫,只有能抓到耗子,就是好猫。

评论

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

这个站点使用 Akismet 来减少垃圾评论。了解你的评论数据如何被处理