Go36-13-结构体及其方法
结构体及其方法
结构体类型表示的是实实在在的数据结构。一个结构体类型可以包含若干个字段,每个字段通常都需要有确切的名字和类型。
结构体类型也可以不包含任何字段,这样并不是没有意义的,因为我们还可以为这些类型关联上一些方法,这里你可以把方法看做是函数的特殊版本。
方法和函数不同,它需要有名字,不能被当作值来看待,最重要的是,它必须隶属于某一个类型。方法所属的类型会通过其声明中的接收者(receiver)声明体现出来。
方法隶属的类型其实并不局限于结构体类型,但必须是某个自定义的数据类型,并且不能是任何接口类型。
方法的使用
接收者声明就是在关键字func和方法名称之间的那个圆括号包裹起来的内容,其中必须包含确切的名称和类型字面量。这个接收者的类型其实就是当前方法所属的那个类型,而接收者的名称,则用于在当前方法中引用它所属的类型的当前值。
举个例子:
// AnimalCategory 代表动物分类学中的基本分类法
type AnimalCategory struct {kingdom string // 界phylum string // 门class string // 纲order string // 目family string // 科genus string // 属species string // 种
}func (ac AnimalCategory) String() string {return fmt.Sprintf("%s%s%s%s%s%s%s", ac.kingdom, ac.phylum, ac.class, ac.order, ac.family, ac.genus, ac.species)
}
在Go语言中,我们可以通过为一个类型编写名为String的方法,来自定义该类型的字符串表示形式。这个String方法不需要任何参数声明,但需要有一个string类型的结果声明。所以在再用fmt包里的函数时,会打印出上面自定义的字符串表示形式,而无需显示的调用它的String方法。
我们可以把结构体类型中的一个字段看作是它的一个属性或者一项数据,再把隶属于它的一个方法看作是附加在其中数据之上的一个能力或者一项操作。将属性及其能力(或者说数据及其操作)封装在一起,是面向对象编程(object-orientedprogramming)的一个主要原则。
匿名字段
下面声明了一个结构体类型Animal,有两个字段,一个是string类型的scientificName。另一个字段声明中只有AnimalCategory,就是上面示例的那个结构体的名字:
type Animal struct {scientificName string // 学名AnimalCategory // 动物基本分类
}
字段声明AnimalCategory代表了Animal类型的一个嵌入字段。Go语言规范规定,如果一个字段的声明中只有字段的类型名而没有字段的名称,那么它就是一个嵌入字段,也可以被称为匿名字段。我们可以通过此类型变量的名称后跟“.”,再后跟嵌入字段类型的方式引用到该字段。也就是说,嵌入字段的类型既是类型也是名称。
强调一下,Go语言中没有继承的概念,它所做的是通过嵌入字段的方式实现了类型之间的组合。
简单来说,面向对象编程中的继承,其实是通过牺牲一定的代码简洁性来换取可扩展性,而且这种可扩展性是通过侵入的方式来实现的。类型之间的组合采用的是非声明的方式,我们不需要显式地声明某个类型实现了某个接口,或者一个类型继承了另一个类型。
同时,类型组合也是非侵入式的,它不会破坏类型的封装或加重类型之间的耦合。我们要做的只是把类型当做字段嵌入进来,然后坐享其成地使用嵌入字段所拥有的一切。如果嵌入字段有哪里不合心意,我们还可以用“包装”或“屏蔽”的方式去调整和优化。
值方法和指针方法
方法的接收者类型必须是某个自定义的数据类型(不能是接口)。所谓的值方法,就是接收者类型是非指针的自定义数据类型的方法。之前的示例中的方法都是值方法。
下面的这个就是指针方法:
func (a *Animal) SetScientificName(name string) {a.scientificName = name
}
方法的接受者类型是*Animal,是一个指针类型。这时Animal可以被叫做*Animal的基本类型。可以认为,指针类型的值就是指向某个基本类型值的指针。指针方法,就是接收者类型是上述指针类型的方法。
值方法和指针方法之间的不同点:
- 值方法的接收者是方法所属类型的一个副本。在方法内对副本的修改一般不会提现在原值上,除非这个类型本身是某个引用类型。而指针方法内对的修改是一定会提现在原值上的。
- 严格来讲,通过值只能调用到值方法,通过指针只能调用到指针方法。但是,Go会适时的进行自动的转义,使得通过值也能调用到它的指针方法。比如,
Animal.SetScientificName("Duck")
会自动转义为(&Animal).SetScientificName("Duck")
,即:先取指针值,然后再在改指针值上调用指针方法。 - 这条和接口相关,一个类型的方法集合中有哪些方法与它能实现哪些接口类型是息息相关的。如果一个基本类型和它的指针类型的方法集合是不同的,那么它们具体实现的接口类型的数量就也会有差异,除非这两个数量都是零。比如,一个指针类型实现了某某接口类型,但它的基本类型却不一定能够作为该接口的实现类型。
这个是验证上述差异的示例:
package mainimport "fmt"type Cat struct {name string // 名字。scientificName string // 学名。category string // 动物学基本分类。
}func New(name, scientificName, category string) Cat {return Cat{name: name,scientificName: scientificName,category: category,}
}func (cat *Cat) SetName(name string) {cat.name = name
}func (cat Cat) SetNameOfCopy(name string) {cat.name = name
}func (cat Cat) Name() string {return cat.name
}func (cat Cat) ScientificName() string {return cat.scientificName
}func (cat Cat) Category() string {return cat.category
}func (cat Cat) String() string {return fmt.Sprintf("%s (category: %s, name: %q)",cat.scientificName, cat.category, cat.name)
}func main() {cat := New("little pig", "American Shorthair", "cat")cat.SetName("monster") // (&cat).SetName("monster")fmt.Printf("The cat: %s\n", cat)cat.SetNameOfCopy("little pig")fmt.Printf("The cat: %s\n", cat)type Pet interface {SetName(name string)Name() stringCategory() stringScientificName() string}_, ok := interface{}(cat).(Pet)fmt.Printf("Cat implements interface Pet: %v\n", ok) // false_, ok = interface{}(&cat).(Pet)fmt.Printf("*Cat implements interface Pet: %v\n", ok) // true
}
这里牵涉到了接口的知识点,所以这个例子和下面的内容,下一篇还会再讲一遍。
最后的2行输出的内容,说明cat没有实现Pet的接口,而&cat是实现了Pet的接口。
因为要实现Pet接口需要实现接下的那4个方法。而Cat类型没有实现SetName方法,所以cat没有实现Pet接口。代码中SetName方法是通过*Cat实现的,另外其他的3个方法都已经通过Cat实现了,通过*Cat也能调用(差异的第2条),所以只有指针方法实现了Pet接口的所有方法。
转载于:https://blog.51cto.com/steed/2340426
Go36-13-结构体及其方法相关推荐
- 005 GO-高级数据类型(结构体和方法)
005 GO-高级数据类型(结构体和方法) http://note.youdao.com/noteshare?id=864cc29d2ca6082949d875280f3f5448&sub=D ...
- C读取配置文件,然后写入结构体的方法
方法一: #include<stdio.h> #include<stdlib.h> #include<string.h>#define MAX 4096 #defi ...
- 【Go】Go基础(八):结构体和方法
一. 结构体(struct) 1.结构体定义 type identifier struct {field1 type1field2 type2... } 声明:new(Type) 和 &Typ ...
- [Go]结构体及其方法
结构体类型可以包含若干字段,每个字段通常都需要有确切的名字和类型.也可以不包含任何字段,这样并不是没有意义的,因为还可以为这些类型关联上一些方法,这里可以把方法看作事函数的特殊版本. 函数事独立的程序 ...
- Rust中对某个结构体实现方法于rust中的关联函数
写法比较独特,但与go语言相差不了太多,都是为结构体单独进行方法的实现: struct Rectangle{width: u32,length:u32, } impl Rectangle{fn are ...
- 结构体+sort方法
昨天做了一道简单但很麻烦的题,我只能想到结构体,并用了STL的sort方法解决了它.不过从中有许多细节问题. 题目: Problem Description Lcy wanted to choose ...
- c++结构体嵌套结构体_Go学习每日一问(13)-结构体嵌套
每次学习并整理一个Golang的知识点,每天进步一点点.今天学习一个go结构体嵌套的知识点. 日省吾身 1.下面这段代码的输出结果? func main() { a := -7 b := +7 fmt ...
- go在方法中修改结构体的值_[Go]结构体及其方法
结构体类型可以包含若干字段,每个字段通常都需要有确切的名字和类型.也可以不包含任何字段,这样并不是没有意义的,因为还可以为这些类型关联上一些方法,这里可以把方法看作事函数的特殊版本. 函数事独立的程序 ...
- 总结几种结构体初始化方法 (转)
结构体能自由组装数据,是一种很常见的数据打包方法.当我们定义一个结构体后,没有初始化就使用,就会使用到垃圾数据,而且这种错误很难发现.对于定义的任何变量,我们最好都先初始化. 除了使用memset和Z ...
- C语言骚操作:结构体初始化方法
首先定义俩结构体: typedef struct {int a0;int b0; }S0;typedef struct {int a1;int b1;S0 s0; }S1; 首先想到的初始化方法是: ...
最新文章
- openGL 坐标系的互相转换
- matlab简单程序实例_【简单实例】如何使用C++加速python程序
- 小程序开发(8)-之跳转第三方小程序设计
- linux put函数,Linux内核中的get_user和put_user
- 国家市场监管总局:低价倾销、大数据杀熟等价格违法行为将被罚
- 垃圾分类逼疯上海人 微信官方终于出手!
- leetcode-反转整数
- C4D多边形建模快捷键
- html电脑添加高德地图,vue-cli项目h5页面或者PC端页面引入高德地图组件,多点标注,自定义弹窗的详细描述...
- win10自带抓包工具_Win10商店抓包工具
- 眼保健操练习方法,眼保健操图解教程
- Aqara绿米董事长游延筠专访:以用户体验为出发点,打造更懂你的家
- img标签图片404异常捕获返回默认图片
- 戴尔电脑录屏怎么录?这3个方法,教你轻松录屏
- 01 基础入门:概念名词
- C语言中 “>>=,<<=,=,^=,|=” 分别表示什么意思? 举例说明
- 企业使用混合云,主要有哪些好处?
- TP link driver - TL-WN823N linux驱动
- express 搭建简易的本地服务器
- 代数几何:Affine Variety 和 Groebner Basis
热门文章
- 01背包,完全背包,多重背包,混合背包,二维费用背包,分组背包,背包问题求方案数
- 【葫芦娃团队】无人转会申请
- “精钢云”落地:鞍钢携手金山云推动中国制造
- Percona 成为 MariaDB 基金会铜牌赞助商
- 代码整洁之道(二)优雅注释之道
- 重置密码解决MySQL for Linux错误 ERROR 1045 (28000):
- insert into 多张表_麦克维尔直流变频多联机弹簧阻尼减震器_淞江集团-李工
- 怎样将Redis以本地服务方式进行启动?
- 怎么在html插入谷歌地图,html页面插入百度or谷歌地图
- java编译环境有问题_java: 用JCeator编译器,编译出现问题怎么解决?